In this post we are going to use an adapted Guestbook application from the Windows Azure Training Kit. This is an excellent demo because it covers all the main topics in one demo:

  • Web roles
  • Worker roles
  • Storage Services

Understanding A Finished Project

hyperlink

http://brunoblogfiles.com/zips/WhackyPhotos.zip

In this post we are going to use an adapted Guestbook application from the Windows Azure Training Kit. I wanted something more colorful and I wanted to play around with some of the code I had laying around.

Purpose of Screen: The main application screen
snap0091[3]
How To Get This Screen:
: Run the application

Simple Projects

The projects are either class libraries or simple ASP.NET web sites. The projects below only involve 2 “File / New / Project” commands.

  • File / New / Project / Windows Azure Cloud Service
    • Generates WhackyPhotos, WhackyPhotos_WebRole, WhackyPhotos_WorkerRole
  • File / New / Project / Class Library
    • Generates WhackyPhotos_Data
Purpose of Screen: Solution Explorer contains all of our projects
snap0090[3]
How To Get This Screen:
: Download and open the project

The reasonable next step is to discuss the data layers, since that is the most significant part of the project. The other parts of this project are absolutely trivial if you know anything about ASP.NET Web Forms.

GuestBook_Data

Most of the functionality is in these three classes that provide the functionality to access and modify data. The rest of this post will address how these 3 classes work together to provide access to our tables and blogs. Remember, tables are simple ISAM file structures that support the LINQ query language. Here are some more facts about table objects:

Table objects

  • Provides Structured Storage
  • Massively Scalable Tables
  • Billions of entities (rows) and TBs of data
  • Can use thousands of servers as traffic grows
  • Highly Available & Durable
  • Data is replicated several times
  • Familiar and Easy to use API
  • ADO.NET Data Services – .NET 3.5 SP1
  • .NET classes and LINQ
  • REST – with any platform or language
Purpose of Screen: Illustrate the data storage layer
snap0092
How To Get This Screen:
: Add a class library and the class modules

Your storage account will contain 1 or more tables. Entities are like rows, but differ in the fact that they are unstructured.

Additional Key Facts
- A storage account can create many tables
- Table name is scoped by account
- Set of entities (i.e. rows)

An Entity is a set of properties, where a property can be likened to a table column.

There are 3 required properties:

  • PartitionKey
  • RowKey
  • Timestamp

Together the RowKey and PartitionKey:

  • Uniquely identify an entity
  • Define the sort order
  • Are used to scale an application.

The timestamp is also required in an entity. Timestamps are:

  • Read only
  • Used for Optimistic Concurrency

Purpose of Screen: Demonstrates the structure of tables
snap0100
How To Get This Screen:
: Your table structure will vary based on the data your are managing

The next screen is about our main data – the GuestBookEntry class, which represents the data that is stored in table objects.

GuestBookEntry.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.StorageClient;


namespace WhackyPhotos_Data
{

    // By inheriting from TableServiceEntity, we can 
    // communicate with an Azure Table Entity in the cloud.

    // TableServiceEntity is a class found in the Storage Client API
    // This class defines the PartititionKey, RowKey and TimeStamp properties 
    // These properties are required by every entity stored 
    //   in a Windows Azure table
    // Together, the PartitionKey and RowKey define the DataServiceKey 
    //   that uniquely identifies every entity within a table
        
    public class GuestBookEntry :
       Microsoft.WindowsAzure.StorageClient.TableServiceEntity
    {
        // Add a constructor
        public GuestBookEntry()
        {
            PartitionKey = DateTime.UtcNow.ToString("MMddyyyy");
            // Row key allows sorting, so we make sure 
            // the rows come back in time order.
            RowKey = string.Format("{0:10}_{1}", 
                DateTime.MaxValue.Ticks - DateTime.Now.Ticks, Guid.NewGuid());
        }
        // Public fields for class
        public string Message { get; set; }
        public string GuestName { get; set; }
        public string PhotoUrl { get; set; }
        public string ThumbnailUrl { get; set; } 

    }

}

Purpose of Screen: The main data storage class
snap0093
How To Get This Screen:
: You will add a class module called "GuestBookEntry.cs"

The data that we wish to manage in a table object is based on a class. In this case, GuestBookEntry is a class that derives from TableServiceEntity.

Table Service Entity

Purpose of Screen: Illustate the main data we are trying to model
snap0097
How To Get This Screen:
: You will add a class module and add the code below

What we are storing in our Table objects

Message, GuestName, PhotoUrl, ThumbnailUrl represents the data that we wish to store in the table object.

Purpose of Screen: Illustrate the core data of our application - will be inserted into a Table object
snap0098
How To Get This Screen:
: See previous screens

NOTE: See previous descriptions of the significance of PartitionKey and RowKey.

Purpose of Screen: Demonstrate the use of the Partition key and Row key
image
How To Get This Screen:
: See previous screens

In order for our data to be available via a REST-ful interface

GuestBookDataContext.cs

class GuestBookDataContext : TableServiceContext
{
    public GuestBookDataContext(string baseAddress, 
            StorageCredentials credentials)
        : base(baseAddress, credentials)
    { }
    public IQueryable<GuestBookEntry> GuestBookEntry
    {
        get
        {
            return this.CreateQuery<GuestBookEntry>("GuestBookEntry");
        }
    }
}
Purpose of Screen: How to expose our data in a REST-ful way
snap0094
How To Get This Screen:
: Part of our data layer

 

GuestBookEntryDataSource.cs

public class GuestBookEntryDataSource
{
    // This class represent the storage account and
    // the data context
    private static CloudStorageAccount storageAccount;
    private GuestBookDataContext context;
    public GuestBookEntryDataSource()
    {
        this.context = new GuestBookDataContext(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
        this.context.RetryPolicy = RetryPolicies.Retry(3, TimeSpan.FromSeconds(1));
    }
    static GuestBookEntryDataSource()
    {
        //Initialize storage account. Use settings from 
        // configuration.
        //Use CreateTablesFromModel to create 
        // tables based on GuestBookDataContext.

        storageAccount =
            CloudStorageAccount.FromConfigurationSetting(
            "DataConnectionString");
        CloudTableClient.CreateTablesFromModel(
            typeof(GuestBookDataContext),
            storageAccount.TableEndpoint.AbsoluteUri,
            storageAccount.Credentials);
    }
    public IEnumerable<GuestBookEntry> Select()
    {
        var results = from g in this.context.GuestBookEntry
                      where g.PartitionKey == DateTime.UtcNow.ToString("MMddyyyy")
                      select g;
        return results;
    }
    public void AddGuestBookEntry(GuestBookEntry newItem)
    {
        this.context.AddObject("GuestBookEntry", newItem);
        this.context.SaveChanges();
    }
    public void UpdateImageThumbnail(string partitionKey, 
        string rowKey, string thumbUrl)
    {
        var results = from g in this.context.GuestBookEntry
                      where g.PartitionKey == partitionKey && g.RowKey == rowKey
                      select g;
        var entry = results.FirstOrDefault<GuestBookEntry>();
        entry.ThumbnailUrl = thumbUrl;
        this.context.UpdateObject(entry);
        this.context.SaveChanges();
    }
}
Purpose of Screen: Illustrate the business layer. Encapsulates the data CRUD operations
snap0095
How To Get This Screen:
: Follow the steps in the previous screens

The final architectural discussion will be about the Developer fabric, which is essentially a simulation environment for the cloud on your local machine.

The Developer Fabric

When you run your project you will be able to go to the system tray to see the developer fabric.

The development fabric simulates the Windows® Azure™ fabric on your local computer so that you can run and test your service locally before deploying it. The development fabric allows you to debug and fine-tune the behavior of your service before it is deployed. It provides a user interface for observing and managing local service deployments.

Purpose of Screen: The system tray and the developer fabric
snap0103
How To Get This Screen:
: Start your project and navigate to the system tray

Showing the Development Fabric and the Development Storage

Purpose of Screen: Options for the developer Fabric
snap0104
How To Get This Screen:
: Right mouse click

Note we have just one instance of our web role instantiated

Purpose of Screen: The user interface for the developer fabric
snap0105
How To Get This Screen:
: See previous step

Note the “Service Details”

Purpose of Screen: Illustrate the details about our running application in Service Details
snap0106
How To Get This Screen:
: Click on the Service Details node

Details about our Development Storage

Purpose of Screen: Illustrate Development Storage
snap0107
How To Get This Screen:
: See previous screen - Part of Developer Fabric

Summary

I hope that gives you a high level view of the architecture of a well-rounded Azure application that leverages all the great features of Azure. We discussed and illustrated:

  • Web roles
  • Worker roles
  • Storage Services