Windows Azure Project Layout – Understanding the Architecture of a Finished Application
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
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 |
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 |
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 |
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 |
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 |
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 |
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 |
NOTE: See previous descriptions of the significance of PartitionKey and RowKey.
Purpose of Screen: Demonstrate the use of the Partition key and Row key |
In order for ourdata 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 |
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 |
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 |
Showing the Development Fabric and the Development Storage
Purpose of Screen: Options for the developer Fabric |
Note we have just one instance of our web role instantiated
Purpose of Screen: The user interface for the developer fabric |
Note the “Service Details”
Purpose of Screen: Illustrate the details about our running application in Service Details |
Details about our Development Storage
Purpose of Screen: Illustrate Development Storage |
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