Jeroen Bernsen's Blog
A blog about using Microsoft .NET Development Technology

The Azure Experiment - Part 1

23 March 2009 21:00 by Administrator

I wanted to try Azure for quite a few months now but didn’t get to it. I have a small game on Internet called Bubbles (see http://www.bernsen.nl/bubbles). The game is written in Silvertlight in an ASP.NET site using AJAX. The highscores are saved in a SQL database using Entity Framework. This is done by calling a WCF service from Silverlight. The logging and exception handling are done using Enterprise Library 4. It seemed like a good test to see how much work it is to port this game to the Windows Azure platform.

In this first part I’ll describe what I had to do to port the just the website (ASP.NET/AJAX/WCF/Entlib) to the Windows Azure platform. I won’t change the datalayer at this time so it will still be using Entity Framework to connect to a SQL Server 2008 database on the internet. In a next blog part I’ll also port the datalayer to Windows Azure (Simple Data Storage Services) or SQL Data Services.

First the Windows Azure CTP (March 2009) can be downloaded at the following location:
• Windows Azure Tools for Visual Studio and SDK: http://go.microsoft.com/fwlink/?LinkId=128752

I created a new Web Cloud Service and put it in my original ASP.NET site.

SQL Server 2008 Developer Edition

Normally you would need to have SQL Server Express Edition installed for Windows Azure SDK BUT….. it can also be configured with developer edition, see http://www.indiangeek.net/2008/10/29/using-windows-azure-with-sql-server-20052008/ . I don’t have SQLExpress installed but I do have SQL Server 2008 Developer Edition. The easiest way is to configure  it with cliconfg.exe. Enable TCP/IP for your local database (default is disabled, also enable the addresses) and create an alias for “.\SQLEXPRESS” using cliconfg.exe, you can map it to your local IP address. This way you don’t have to change any config files at all (you don’t want to do that for every new release).

Enterprise Library

I then wanted to port the Enterprise Library stuff to Azure. My first thought was it should be real simple just build a Custom Trace Listener for the logging to log to the Azure platform but I ran into a few small problems:

1) First set requirePermission to false for the Entlib section:

<section requirePermission="false" name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> 

2) I like to put my EntLib config in a separate file, but this doesn’t work in Azure because Azure doesn’t give you the FileIOPermission to read it, understandable but I had to debug Entlib to find out the exact problem. So put all entlib configuration data in the web.config.

3) I used the Azure RoleManager.WriteToLog to do the logging, but it cannot be used in the Application_Start event. So you cannot log start of your web application there, you have to move it to for example BeginRequest (but make sure it is done only once).

For Entlib logging I build a CustomTraceListener which uses the Azure RoleManager to do the logging. See code below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using System.Diagnostics;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
using Microsoft.ServiceHosting.ServiceRuntime;
 
namespace BlueEdge.EnterpriseLibrary.Logging.Configuration
{
    [ConfigurationElementType(typeof(CustomTraceListenerData))]
    public class AzureTraceListener : CustomTraceListener
    {
        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
        {
            if (data is LogEntry && this.Formatter != null)
            {
                this.WriteLine(this.Formatter.Format(data as LogEntry), eventType);
            }
            else
            {
                this.WriteLine(data.ToString(), eventType);
            }
        }
 
        public override void Write(string message)
        {
            WriteLine(message, TraceEventType.Verbose);
        }
 
        public override void WriteLine(string message)
        {
            WriteLine(message, TraceEventType.Verbose);
        }
 
        /// <summary>
        /// Write to Azure log
        /// </summary>
        /// <param name="message"></param>
        /// <param name="eventType"></param>
        protected void WriteLine(string message, TraceEventType eventType)
        {
            string eventLogName = eventType.ToString();;
 
            // Allowed eventlog names are: Critical, Error, Warning, Information, Verbose
            switch (eventLogName)
            {
                case "Critical":
                case "Error":
                case "Warning":
                case "Information":
                case "Verbose":
                    break;
                default:
                    eventLogName = "Information";
                    break;
            }
            
            RoleManager.WriteToLog(eventLogName, message);
        }
 
    }
}

You can configure it like this:

<loggingConfiguration name="Logging Application Block" tracingEnabled="true"
    defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
    <listeners>
      <add formatter="Text Formatter"
        listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        traceOutputOptions="None" filter="All" type="BlueEdge.EnterpriseLibrary.Logging.Configuration.AzureTraceListener, BlueEdge.EnterpriseLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
        name="Your TraceListener" />

 

Configuration

I like to make the configuration dependent on the hostname or environment (local / staging / production). Hostname is not an option in a cloud service and for now it is also not possible to determine the environment (local/staging/production) so for now I have put the configuration information just in the ServiceConfiguration.cscfg.

Conclusion

Porting an existing webapplication to Windows Azure is fairly simple for the presentation layer. ASP.NET, AJAX, WCF, etc all runs the same without modifications. Things that don't work are accessing you local machine environment e.g. filesystem, eventlogging, hostname. For now the application still uses the same datalayer using a remote SQL Server 2008 database. In my next experiment I'll rewrite the datalayer to Windows Azure Simple Data Storage Services.

Comments