Introduction

Silverlight 3, .NET RIA Services, and Windows Azure Services Platform makes a 3-tier Cloud application easier to build: Silverlight 3 as presentation tier, .NET RIA services as the business logic and data access tier, and Windows Azure Table as the data storage tier. The sample application in this article demonstrates the architecture with a simple Survey application with all these technologies working together from Windows Azure.

A similar architecture has been discussed in some articles and posts. One article in MSDN (Data Services:Access Your Data On Premise Or In The Cloud With ADO.NET Data Services) has great discussions about accessing Windows Azure Table storage either on premise or in the Cloud by ASP.NET MVC, but it doesn’t involve Silverlight 3 or .NET RIA Services. Another article on the same issue on MSDN (.NET RIA Services:Building A Data-Driven Expense App with Silverlight 3), has a great sample application that leverages Silverlight 3 and .NET RIA Services; while its data model is EDM (Entity Data Model) generated from Entity Framework based on an on-premise database, it doesn’t involves Azure Table storage or Windows Azure. A blog post from Nikhil Kothari (Nikhil Kothari's Weblog: .NET RIAServices MIX '09 Talk - Slides...) is the closest to what I’m trying to achieve in terms of architecture, but his sample code is not an Azure project, and doesn’t cover the caveats when the application is put on Windows Azure as a hosted service. So, I decided to write down the techniques and caveats I learned when I built the Azurelight application: a Windows Azure application built using Silverlight 3, .NET RIA Services, and Azure Table storage.

Brief on 3-Tier RIA in the Cloud

A tier in an application implies physically self-contained binaries that promote loose-couplings, reusability, and testability. It’s conceptually different with layers, which is a logical code structure that manages dependencies. When a Rich Internet Application needs application-wide persisted data, it usually has at least two tiers: the presentation tier is physically separated by nature (downloaded and executed in the client at runtime) and a data storage tier. We are not discussing persisting data in the client, that’s Isolated Storage, which usually is more about user-specific data. The persistence discussed here is about application-wide data that usually is stored in a database server.

A middle tier usually sits between the presentation tier and the data persistence tier, it runs on the server, serves the client request, and handles most application business logic and data access (CRUD) logic to the underlining data store.

Currently, most service-oriented architected RIAs are designed in this model: a rich interactive client talks with a middle business logic tier through Web Services. The implementation of the Web Service interface performs the business logic and handles data CRUD operations to and from the database. Presentation, Business Logic and Data Persistence tiers are separately designed and developed, often in different technologies and platforms.

The 3-tier RIA structure experimented here is different from the above SOA, since one of the design philosophies of .NET RIA Services is to consider the server side business logic and data access logic are part of the same application as the presentation tier. This is a very different thinking comparing to loose-coupled SOA; it sacrifices some design time loose coupling (server side data model is “projected” to the client at design time), but gains productivities:

Application data model, at least DTOs (Data Transfer Objects) or VOs (Value Objects), just need to be defined once at the server project and are then available at the client project automatically, saving data model coding efforts and avoiding the client side model being out of sync with the server objects.

The metadata of the application data model is also “shared” between the client and the server; it helps to make sure the client side and the server side data validation logic are the same. Together with the naming convention based code sharing, validation logic is written once and executed twice, once in the client and ocne in the server.

With the help of the new Silverlight 3 data-centric controls, DataForm, DataGrid, DataPager, etc., the projected client side application data model can be directly bound to these controls, and the controls can generate UI elements (labels, fields, data entry error handing). This could make quick prototypes becomes possible.

The third tier, data storage, is often a database server in traditional SOA based RIAs. When putting the application to Cloud, ADO.NET Data Services and SQL Azure can help to make data access easier and scalable. For some applications, the data model only needs some preliminary structure, while not demanding relational entities between objects, the data storage layer can bypass the database and data service to utilize Windows Azure Storage. For this type of applications, Window Azure is not only the hosting environment, but also a scalable data storage provider, like the file system's role in desktop applications.

For example, a Cloud based file management solution can leverage Blob Storage in Windows Azure for its data, all data getting persisted, but not requiring a database server to do the work, since those unstructured data (files) has very limited relational entities to manage. Another example is a Cloud based Notification application. Windows Azure Queue can help with the reliability and scalability; using a database might be overkill. A third example is that an application’s data model has very few or no relational entities (like foreign key references, many to many relationships, etc.), but has some sort of structure within the entity, and so it’s suitable to use Window Azure Table Storage.

The sample application in this article fits into the third category, it collects and displays a simple survey from the end user, and the user can specify a rating and give comments as a survey entry. The data model for survey (the Survey class) has a structure (rating and comments, etc.) while doesn’t have referencing relations with other objects. We can use Windows Azure Table as its data store, and no database is needed.

What we saw above is a high level discussion about the application architecture. Let’s look at some details about how to build it with Silverlight 3, .NET RIA Service, and Azure Table, and then we’ll deploy it to Azure.

Getting Started

As mentioned above, our goal is to build an Azure hosted web application that can display feedback from all users; each feedback entry contains rating and comments fields, and the user can update an entry and also create new entries. In case other users add new entries, the application should provide a refresh function to reload all the persisted feedback entries. After we build the application, we’ll deploy it to Cloud: Windows Azure.

Because the Silverlight 3 SDK removed the ASP.NET Controls for Silverlight (Silverlight control and MediaPlayer control), the System.Web.Silverlight.dll needs to be downloaded from MSDN Code Gallery. It's included in the downloadable source code.

Beyond the obvious, it’s helpful to have a Windows Azure account and a Windows Azure Storage account set up when building an Azure project with Azure Table. Although technically, we should be able to develop and debug an Azure project with a local fabric and development storage (requires SQL Server 2008 Express), but I had lots of trouble working with local development storage for Azure Table, like exceptions as “Not able to connect to the server…”, “object doesn’t exist”, or simply failed silently ---- the data is just not there without any error message. After I created the Azure Storage Account and configured the sample application to use the “real” Azure table rather than the local simulation, everything moved smoothly. If you still want to get your hands dirty with the local development storage, the SQL scripts and database file are also included in the downloadable source code package.

The sample application is created using Cloud Service -> Web Cloud Service with a Web Role template in Visual Studio and named Azurelight. Then, we need to create a Silverlight 3 project (named AzurelightNav) based on the Silverlight Navigation Application template (note: not the Silverlight Business Application template installed by the RIA Services) and specify the Azure project’s WebRole ASP.NET project (named Azurelight_WebRole) as its hosting application and link to it.

After the initial start up, we also need to add the Azure class library to the solution. This is the class library included in Nikhil Kothari's Weblog download. It handles the low level communications with Azure Table Storage and provides the implementation of AzureDomainService and Azure Table based DataContext, Entity, and EntitySet types. This is the reason we didn’t use the Silverlight Business Application template to start our AzurelightNav Silverlight 3 application, and also, we don’t need to include the StorageClient class library from the Windows Azure SDK.

Since the server side data model will be “projected” to the client side as generated code by .NET RIA Services and the compiler, we need to set the client project (AzurelightNav) to depend on the server project (Azurelight_WebRole) in the solution’s Properties page. Otherwise, a compiler error will be generated after we define the model (client proxy is not generated).

Now that we have a skeleton of the Azure project up and running, let’s look at some details.

Configure the Services

Within the Azurelight Azure project, we need to add the following settings to the ServiceDefinition.csdef XML to enable the Azure Table configuration settings:

The caveat in the service definition is not the three new configuration settings, it is that the enableNativeCodeExecution needs to be explicitly set to be true for the WebRole. It essentially enables the full trust mode for the RIA Service on Azure. The default service definition has it to be false; RIA Services will not work with the default setting. Till .NET RIA Services is built on top of .NET 4.0, we need to explicitly enable it; see here for further information.

The actual value for these new configuration settings are set in the ServiceConfiguration.cscfg XML file:

Just like what we discussed in Getting Started, I recommend to set up a Windows Azure Storage account to develop an Azure Table based project, instead of using the local development storage, to avoid some headaches that I ran into. As for the settings, the AccountName is not your Azure Portal account name, but the name of your storage on Windows Azure. The AccountSharedKey should be the Primary Access Key as listed in the summary page of your storage project in the Azure developer portal.

The account name and the account shared key also need to be set in the WebRole ASP.NET project’s Web.config file to enable the Azure class library to access the Azure Table account information:

The caveat here is about the “uri=” part: it must always be http://table.core.windows.net, or the runtime will throw an exception complaining it can’t find the service. This is not the same as the URL listed in Azure Portal about your table endpoint URL. That one has the account name, like http://azurelight.table.core.windows.net.

The storage account name, primary access key, and the shortened version of the table storage end point are crucial to access Azure Table storage from your project. Once we have the Azure Project and the WebRole ASP.NET configured, our data storage tier is ready to go, and we can move on to coding.

Define the Data Model and DataContext

Defining the application data model and the DataContext in the WebRole project is the first step to build our middle tier – the business logic tire. With .NET RIA Services and Azure Table storage, we can take the model-centric approach: define the application model first, then rely on the infrastructure to generate the Azure Table Structure based on our data model, since Azure Table doesn't require a pre-defined fixed data schema, while the database does.

Since our application domain is to collect and display the user’s feedback, we can create the simple Survey class as our data model; the Survey.cs file is located under the folder named Azurelight_WebRole/Model.

The base class, Entity, is an abstract class defined in the Azure class library. It makes sure all the derived types will have PartitionKey, RowKey, and time stamp properties and attributes. Those are required fields by Azure Table. Our derived type, Survey, can focus on the domain model needs, rather than the underlining Azure Table specifics. In order to let Silverlight 3 DataForm understand our intention to auto-generate data fields for input, we need to set the correct Display attribute in the base Entity class (in the Azure class project).

Since the Survey class is very simple, we don’t need a partial class to define its metadata; they are all defined in the same class file. Those metadata will be “projected” to the client, and the Silverlight 3 DataForm control will generate the corresponding UI elements based on those metadata. And also, the default error handling for data validation (rating is required and must be within the specified range) will honor those metadata too.

[Bindable(false)] and [Display(AutoGenerateField = false)] essentially tell the DataForm control not to generate an input field for the property; it’s only used by business logic, and should be transparent for the end user. For example, the Target property could refer to an application name, then our Survey class can be used to collect feedbacks for different applications. And also, the PartitionKey, RowKey, and Timestamp are all Azure Table specific fields, we don't want to expose them in the user interface.

With the context of .NET RIA Services, the application data model needs to be wrapped into a DataContext derived type to adapt CRUD operations. Our SurveyDataContext.cs file is located under the same folder as where Survey.cs is:

Again, the base class DataContext is abstract and defined in the same Azure class library; EntitySet is not abstract, its definition is in the same library. This is the Azure Table specific DataContext and EntitySet.

After we have a data model and data context, we can define our domain service type, which is where the business logic is exposed to the client for operations.

Create the Domain Service

The second step to develop our middle tier is to wrap the DataContext by a DomainService. The DomainService will expose data/service operations to clients and delegate some operations to the DataContext (the SurveyDataContext we defined in the last section). We are still with the WebRole project, the domain service type is created under the Service folder:

Just as the DataContext type, the base AzureDomainService is abstract and comes from the Azure class library. This is an Azure Table storage specific DomainService, it doesn’t support the Azure Blob or Queue. It’s important to override the GetPartitionKey method; it affects the PartitionKey for all new entities to be stored in the Azure Table; it also affects the client query parameter.

Our implementation follows the "convention over configuration" approach. The data operation method names are GetXXX, AddXXX, and UpdateXXX. They’ll be “projected” to the client generated code. If configuration is preferred, you can take the arbitrary method names then apply the [Query], [Create], [Update], and [ServiceOperation] attributes to them. They’ll also be projected to the Silvelright 3 project.

Notice that most the data operations are delegated to the data context object. If your middle tier needs to expose domain-specific operations, you can define and implement them here and apply the correspondent [ServiceOperation] attribute, so they’ll be visible to the client. For simplicity, this sample app doesn’t include other domain specific operations.

Within the body of the AddSurvey method, we set the RowKey and TimeStamp to the survey object, then invoke the data context to add it to the list. The caveat is not to set the PartitionKey, or the query (GetSurveys) won’t find any entity, since the client query will use the return value of the GetPartitionKey; if they are out of sync, you’ll have the problem that I had: saving always succeeds, but all queries return an empty list in the client.

Because the nature of the Survey application is to collect all kinds of feedback from the end user, we intentionally left out the Delete operation from the domain service. The end user can only view, add, and update feedback (ratings and comments) entries, not remove.

Another caveat is how to instantiate the instance of the domain service. I used to put the instantiation code in the Global.asax.cs::Application_Start event handler, but starting debug will pop up an error message: “instances did not start within the time allowed”. Base on a post in MSDN, I moved the instantiation code to Application_BeginRequest, then the error message disappeared. Please check out Global.asax.cs in the Azurelight_WebRole project for details.

Now that we have the data model, metadata, data context, domain service, and a working mechanism to instantiate them, compiling the project will “project” the model and the service to the client project (AzurelightNav). Let’s move our focus onto the presentation tier next.

Using the Projected Code in Silverlight

The compiler will create a hidden folder named Generated_Code in our client AzurelightNav project, and put the .NET RIA Services projected code (Azurelight_WebRole.g.cs) there. The Survey (as data model) and SurveyContext (as the delegate interacting with the server) are automatically available to the Silverlight code at development design time.

The sample project size is pretty small, we don’t really need a client side framework. But for demonstration purpose, I still added SilverlightCairngorm as the client side framework. It’s only 26K, and helps on separation of concerns for the Silverlight code. Since the projected/generated SurveyContext already handles the interaction with the service, we don’t need any Cairngorm Delegate code; all Cairngorm Commands will execute the methods of SurveyContext.

Since we’re using Silverlight Cairngorm, we won’t create the instance of SurveyContext in XAML by the DomainDataSource control, but rather, we have a read-only property inside AzurelightModel; it’ll serve as the root of all the survey data related binding data context in our views.

The Feedback property is the data context for creating new and updated existing survey entries; CreateFeedback() is a factory method to make sure all new Survey instances will have a consistent target value across the application; the surveyLoaded property is just a flag indicating the survey list data is already loaded from the service or not. All other details for AzurelightModel can be found at AzurelightNav/Model/AzurelightModel.cs in the downloaded source code.

Since we have the Model defined, we can create Views and data bind to the Model. Our primary view is Feedback.xaml (under the Views folder). It has a DataGrid to display all the survey entries from the Azure Table (via SurveyDataContext) and also three buttons to allow users to refresh, add, or update survey entries:

AutoGenerateFields="True" in the XAML tells the DataForm control to generate data entry labels and fields based on the type of the feedback, which is the Survey class we defined in the WebRole project. Its public bindable properties will auto-generate data bound fields, and metadata like [Required] and [Range] will trigger the visuals (bold label for required fields) and validation logic (error summary at the bottom and red border around the field when validation fails).

In the code-behind, it has a working mode for adding and updating, and when you commit the editing with no error, the correct Cairngorm Event will be raised to add or update:

As shown above, all event handlers code just dispatch different Cairngorm events; they don’t hold and interact with the SurveyDataContext directly. The event will be routed by AzurelightController (AzurelightNav/Control/AzurelightController) to the corresponding command object to perform the actual work.

All commands are implemented under the AzurelightNav/Command folder; they just simply delete the method in the RIASvc property of AzurelightModel. SurveyDataContext handles all the serialization, request and response events, de-serialization when receiving a response, and updated the property with load data. When a property is updated, the Silverlight Data Binding engine will notify the view to update itself automatically. With the help of the .NET RIA Services projected code, the presentation tier’s plumbing works are reduced and simplified greatly.

The base class, FeedbackCmdBase, defines common properties (model, progress dialog, event handler, etc.) and also handles SubmitChanges (then it can be shared by the PostFeedback Command and the UpdateFeedback Command):

Please check out the code in the Command, Control, and Model folders for all the details.

Up this point, we have a functioning application running Silverlight 3, .NET RIA Service, Azure Table storage at our local development machine. After testing, right click on the Azure project in Visual Studio, then select “Publish…”, and the deployment process to Windows Azure is pretty obvious.

Wrapping up

This is an experiment sample project for RIA Services and Azure Table in Cloud. It demos the productivity gains when the application data model and the meta data is defined once, but is available for both the server and client side code, including auto-data entry fields generation, auto-error handling in the client, and the same validation logic written once and executed both in the client and server. But the developer experience can be improved as these new technologies evolve, like the concerns about the coupling in the service code and the client code, the required full-trust mode when running on Azure, the lack of official supporting libraries from the Azure SDK, hosted services Application ID decoupling, etc. I am looking forward to better services and support in the future.

The updated source code actually becomes cleaner than before. I removed the Azure library project from the solution, also the previous Entity, EntitySet, DataContext and AzureDomainContext types are all removed. Instead, it uses the new TableServiceEntity, DomainServices, TableServiceContext, StorageCredentials, CloudTableClient types that available from Azure Tools Nov.2009 release. The mid-tier that handles Azure Table changed significantly while Silverlight front end stays pretty much the same as before. And the updated code works with the exisitng data that already in Windows Azure Tabel Storage.

I was planning to document the changes in details, but haven't find time to do so yet. If you need the updated source code, you can download it from:

If you are using latest (so far) Azure SDK (Nov CTP), You will get a few compile errors. To fix them just modify the following code.

1. In StorageAccountInfo.cs Line 309. Replace the current code to
if (RoleEnvironment.IsAvailable)
The RoleManager is not long existing in Nov CTP. It is replaced by RoleEnviroment and is found in Microsoft.WindowsAzure.ServiceRuntime.

Search for all RoleManager, replace them all

2. Reference to new Microsoft.WindowsAzure.ServiceRuntime and Microsoft.WindowsAzure.StorageClient, and using them in the code

3. Replace the account name and key in AzureLight_WebRole and Web.Config with your real account name and key

I was set to update the code with VS 2010 Beta2 and Azure Nov.2009 release, only found out Windows Azure doesn't support .NET 4.0 yet. So I updated the code in VS 2008 SP1 with the Silverlight 3 Toolkit November 2009, Silverlight 3 Tools for Visual Studio 2008 SP1, WCF RIA Services Beta (11/18/2009), and Azure tools for VS 2008 SP1 (November 2009).

The source code actually becomes cleaner than before. I removed the Azure library project from the solution, also the previous Entity, EntitySet, DataContext and AzureDomainContext types are all removed. Instead, it uses the new TableServiceEntity, DomainServices, TableServiceContext, StorageCredentials, CloudTableClient types that available from Azure Tools Nov.2009 release. The mid-tier that handles Azure Table changed significantly while Silverlight front end stays pretty much the same as before. And the updated code works with the exisitng data that already in Windows Azure Tabel Storage.

I was planning to document the changes in details, but haven't find time to do that yet. If you need the updated source code, feel free to contact me.

As I reported I didn't manage to run this example on VS2010.
With VS2008 I was more successful. Still I have two remarks on source.
1. Microsoft.Expression.Prototyping.Runtime.dll is referenced but missing (example runs and compiles without it). I guess this if for MS Design Tool plugin?
2. System.Windows.Controls.Data.DataForm.Toolkit.dll reference problem
This dll, I found in C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ProjectTemplatesCache\CSharp\Silverlight\1033\BusinessApplication.zip\Libs example. Update reference and .
Now is working!

BTW Trial image of VS2008 an Microsoft is not working because it uses Windows Server 2003. We need Vista or Windows 7.

Some errors:
Error 1 The type or namespace name 'Service' does not exist in the namespace 'Hanray.Azurelight' (are you missing an assembly reference?) Azurelight\AzurelightNav\Command\FeedbackCmdBase.cs 13 25 AzurelightNav
Error 2 The type or namespace name 'Ria' does not exist in the namespace 'System.Windows' (are you missing an assembly reference?) Azurelight\AzurelightNav\Command\FeedbackCmdBase.cs 14 22 AzurelightNav
...

So glad to know you see azurelight too!.NET RIA Services has very different design philosophy comparing to conventional design principles...and Azure has lots of differences from Amazon's EC2...so many new stuffs out there...

Thanks all the feedbacks, both the feedbacks in here and those in the sample app!

2nd day of the initial post, SL3 is released. With breaking changes, as expected. The sample application and downloadable code are updated with
* Silverligh 3 RTW
* Silverlight 3 Tool Kits July 2007 (DataForm is moved from SDK to Toolkit)
* .NET RIA Services July 2009 Preview