Windows Azure-hosted ASP.NET MVC Site

16 July 2012

Summary:
In this post I discuss my experiences with creating an ASP.NET MVC 4 web site and then hosting it on Windows Azure.

Introduction

With the cloud in general, and Windows Azure in particular, gaining lots of attention recently, I decided to have a go at creating a relatively simple web site (that holds blog posts) and hosting it on Azure. I wanted the main elements of the site to be:

  1. Develop using ASP.NET MVC 4
  2. A modern design, loosely based on Windows 8 Metro (e.g. "live-tiles")
  3. Data served via static JSON file, later to migrate to SQL Server via the Entity Framework
  4. Make use of jQuery and jQuery plugins as necessary

Here's a snapshot of how the site ended up looking:

Azure Sign-Up

Microsoft make signing up for Windows Azure extremely easy, straight-forward and worry-free. First, you need to sign up for the 3-month web site trial. This allows you up to 10 Azure-hosted web sites free for 3-months, with the option to extend that free period to a full year. This is great, and it addresses what I guess would be the number-one issue for developers wanting to try out Azure: will I suddenly get lumbered with a large bill because I don't fully understand the service charge nuances!

Once you've signed up, open the Azure Dashboard, select the Web Sites option and click New:

I used the Quick Create option, which simply requires you to supply a name for the site. This name has to be globally-unique on Azure, and you'll be prompted if you provide the name of an existing site.


It's also possible to create a new site based on a template using the From Gallery option. This provides you with access to templated sites based on technologies such as Acquia Drupal, DotNetNuke, etc.

Once you hit Create Web Site the bare-bones of the site are created in the data centre for the geographical location of your choice (I chose North Europe). The Dashboard will then show the details of the site:

Hooking up Visual Studio

The next thing to do is create your web site project and set up Visual Studio to publish directly to the Azure-hosted site. Simply create an ASP.NET MVC site in the normal way. I chose the Basic web site.

Important: Once Visual Studio has created the site, open the Project | Properties window, select the Application tab and change the target framework from the default .NET Framework 4.5 to .NET Framework 4:

You need to do this because, currently, Azure only supports .NET version 2.0 and 4.0. You can see this from the Azure management portal, Configure tab:

Still on the Azure portal, select the Dashboard tab and click Download publish profile:

The publish profile is a text file that contains all the authentication and other details required to allow Visual Studio to connect to your site on Azure. To import the publish profile into Visual Studio, right-click the project in Solution Explorer and select Publish. Click Import and then navigate to the downloaded publish profile. You can now review the various connection settings:

When you're ready, select the Preview tab. You can preview which files have changed and will be published by clicking the Start Preview button:

You can then publish the changed files by clicking Publish:

Once setup, I found the publishing process to be very quick, easy and trouble-free.

Site Design

The simplicity of the site - it's basically just a list of blog posts - meant that all I really needed to concentrate on was:

  • Getting the right "Metro-like" style
  • Live-tiles (using the MetroJS jQuery plugin)
  • Adding custom CSS and JavaScript to the MVC bundling scheme
  • Consuming the post-list as JSON
  • Adding posts as partial views, loaded via a PostContent view

Live-tiles

After some minimal changes to the standard Site.css and basic layout of the site, I moved on to look for a nice jQuery plugin for doing metro-like "live-tiles". After a Bing search I came across MetroJS, which suited my requirements very nicely! All that's required to create a live-tile is to add one or two div's with the live-title class attribute and the following JavaScript:

  $(function () {
      // Turn on live tiles
      $(".live-tile").liveTile();
  
      // Change any tile's state when there's a mouseover
      $(".live-tile").mouseover(function ()
      {
          $(this).liveTile("animate");
      });
  });

The following example HTML shows the basic layout on the page:

  <div class="blue live-tile">
      <div><img src="~/Images/code1.png" /></div>
      <div>
          <p>My live tile text!</p>
      </div>
  </div>

Adding custom CSS and JavaScript to the MVC bundling scheme

As you may know, ASP.NET MVC 4 uses a bundling and minification scheme, so that references to lots of individual .js and .css files get combined and minified (all the whitespace removed, etc.) into a request for single "bundle" file. This has major benefits in terms of performance. You can read more about the scheme here.

I added my custom .js and MetroJs.js to the projects' Scripts folder. I also added MetroJs.css and the associated images folder to the project's Content folder:

If you look in the App_Start project folder, you'll see BundleConfig.cs. As the name suggest, this controls which files are bundled:

  public class BundleConfig
  {
      public static void RegisterBundles(BundleCollection bundles)
      {
          bundles.Add(new ScriptBundle("~/bundles/jquery").Include("~/Scripts/jquery-1.*"));
          bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include("~/Scripts/jquery-ui*"));
          :
          :
          bundles.Add(new ScriptBundle("~/bundles/metrojs").Include("~/Scripts/MetroJs*"));
          bundles.Add(new ScriptBundle("~/bundles/rpa").Include("~/Scripts/rpa*")); 

          bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));
          :
          :
          // Make sure to add the trailing "/css" to the StyleBundle path:
          bundles.Add(new StyleBundle("~/Content/MetroJs.Full.0.8.9.3g/css").Include(
            "~/Content/MetroJs.Full.0.8.9.3g/MetroJs.css"));          
          :
          :

Note that ASP.NET MVC creates the bundle during the call to Application_Start in Global.asax:

  protected void Application_Start()
  {
      AreaRegistration.RegisterAllAreas();

      FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
      RouteConfig.RegisterRoutes(RouteTable.Routes);
      BundleConfig.RegisterBundles(BundleTable.Bundles);
  }

Consuming the post-list as JSON

I decided to initially keep the site's requirements simple (i.e. no potentially expensive database) by defining the list of blog posts as a static text file (stored in a project folder named Data) in JSON format:

  [
    { 
        "Id":"0", 
        "Url":"_WcfSimpleService", 
        "Publish":"01 May 11", 
        "Title":"Intro to WCF - Part 1 - A Simple Service", 
        "Description":"WCF Simple Service", 
        "Tags":""
    },
    { 
        "Id":"1", 
        "Url":"_WcfSelfHosting", 
        "Publish":"02 May 11", 
        "Title":"Intro to WCF - Part 2 - Self-Hosting", 
        "Description":"WCF Self-Hosting", 
        "Tags":""
    },
    :
    :
  ]

I then created a blog post model:

  using System;
  using System.Collections.Generic;
  using System.Runtime.Serialization;
  using System.IO;
  using System.Net;
  using Newtonsoft.Json;
  
  namespace rarcher.azurewebsites.net.Models
  {
      [DataContract]
      public class Post
      {
          [DataMember]
          public string ID { get; set; }
  
          [DataMember]
          public string URL { get; set; }
  
          [DataMember]
          public string Publish { get; set; }
  
          [DataMember]
          public string Title { get; set; }
  
          [DataMember]
          public string Description { get; set; }
  
          [DataMember]
          public string Tags { get; set; }
  
          public Post() { }
  
          public static List<Post> GetJsonData(string jsonURL)
          {
              string data;
              StreamReader reader;
              List<Post> postList = null;
              Uri uri = null;
  
              try
              {
                  // *** Note that caching code (via HttpRuntime.Cache) *** 
                  // *** has been removed for clarity                   *** 

                  uri = new Uri(jsonURL);
                  var request = HttpWebRequest.Create(uri) as HttpWebRequest;
                  request.Method = "GET";
                  request.ContentType = "application/json";
  
                  var response = request.GetResponse() as HttpWebResponse;
                  if (response.StatusCode == HttpStatusCode.OK)
                  {
                      using (reader = new StreamReader(response.GetResponseStream()))
                      {
                          // Read the entire json content as a single string
                          data = reader.ReadToEnd();
                      }
  
                      // Use Json.NET to deserialize the json array...
                      if (!string.IsNullOrEmpty(data))
                          postList = JsonConvert.DeserializeObject<List<Post>>(data);
                  }
              }
              catch
              {
                  postList = null;
              }
  
              return postList;
          }
      }
  }

The [DataContract] and [DataMember] attributes were used to assist with the deserialization of the JSON. The only complexity in the class is in the static GetJsonData() method. As you can see from the above code, an HttpWebRequest is made, and if the server responds with an "OK" code, a StreamReader is used to read the file as a string. The JsonConvert.DeserializeObject() method in the Json.NET package (which is installed as part of the project by default) is used to convert the text into a List of Post objects.

A simple PostController class with an Index action displays a list of links to posts, and a PostContent action displays a particular post.

Adding posts as partial views, loaded via a PostContent view

The final piece of work was to have the PostContent view reference the required partial view, which contains the actual blog html:

  <article>
      @Html.Partial((string)ViewBag.URL)
  </article>

Next Steps

The next steps are to modify the site to support SQL Server, and as part of that, to abstract-out controller dependencies on a particular data source. I'll be writing about this process in my next post.

Conclusion

I found working with Azure to be a remarkably painless and straight-forward experience! The integration of publishing profiles with Visual Studio really helps to increase developer productivity, as there's no jarring need to break out "developer-mode" into "FTP-mode" to upload files to the cloud. The simple, but informative Azure portal enables the developer to see at a glance how much disk space, memory, CPU, etc., has been used. And with the option to host up to 10 web sites free for a year, I found Windows Azure a real pleasure to work with. Thank you Microsoft!