We’ll now tackle one of the most essential labs in the WATK, Introduction to Cloud Services (in your WATK folder under \Labs\IntroToCloudServices-VS2012). This lab uses a full gamut of classic PaaS services: web roles, worker roles, tables, blobs, and queues. And though the manual doesn’t call it a CQRS solution, that’s precisely what it is, demonstrating how readily Azure can support CQRS architecture.
For convenience we’ll use the C# cloud service template to create the ASP.NET web role, but we’ll still write most of the code in F#. We’ll do Exercise 1 in this post and Exercises 2 and 3 in the next post.
We can skip the Setup in the lab manual (unless you want it to check prerequisites), but we do Task 1 exactly as the manual says (though I named my solution “GuestBook” instead of “Begin”). This gives us a web role named GuestBook with a C# web forms project named GuestBook_WebRole. To carry out Task 2, we add an F# library named GuestBook_Data to the solution.
We can delete the files Library1.fs and Script.fsx and remove the reference to System.Numerics in Solution Explorer. The lab requires us to add a reference to Microsoft.WindowsAzure.StorageClient and System.Data.Services.Client. These libraries lead to an OO style of coding in F#; in future posts I’ll look at more functional approaches.
The rest of Task 2 consists of writing three classes for managing table storage. Because System.Data.Services.Client follows the same ORM pattern as Entity Framework, the code is similar to the EF classes in Part 3. The GuestBookEntry class is a simple DTO with mutable properties.
Like its EF counterpart in Part 3, GuestBookDataContext inherits from a base data context class that does most of the work.
To finish Task 2 we add the GuestBookDataSource class, which manages hooking up a GuestBookDataContext to an Azure storage account and provides CRUD methods. This class also calls CreateTablesFromModel, though as the manual notes, GuestBookDataContext could have done that instead (as in Part 3).
A very important, but subtle, change from the manual’s C# version is the Linq expression in GetGuestBookEntries(). If you call DateTime.UtcNow inside the .Where clause, as shown in the manual, you’ll get a scary runtime exception.
Under the hood the Azure SDK is converting the Linq expression to a RESTful call to the storage service, and it appears that the REST API doesn’t support DateTime.UtcNow. F# refuses to bail us out by silently executing part of the Linq expression locally. So we must call DateTime.UtcNow locally on a separate line and plug the result in the Linq expression. It’s worth that small effort since, in return, F# will not mislead us into thinking we’re running Linq code remotely when in fact we aren’t.
We’ll put most of the code for Tasks 3 and 4 in two new F# projects instead of in the Default.aspx.cs file. This is both an excuse to write more of the code in F# and a recognition that putting this kind of state management and workflow in a code-behind is not very SOLID, even for a HOL. We’ll create a separate project to manage Azure storage and another to implement the client-side workflow.
We begin Task 3 by creating an F# project named GuestBook_Storage. We prepare it the same way as we did GuestBook_Data, except we only need to add a reference to Microsoft.WindowsAzure.StorageClient. We add an F# source file named StorageSource.fs.
Since the original C# code used static storage objects, we get nearly the same result in F# with a module, which compiles down to a static .NET class. The initializeStorage function follows the C# original but leaves responsibility for thread locking and exception handling to the caller, making the module more re-usable. The rest of the methods give access to storage functionality without exposing the storage objects.
Our next F# project is named GuestBook_WebApp. We prepare it the same way as GuestBook_Data and add references to the GuestBook_Data and GuestBook_Storage projects. We add the source file GuestBook_Controller.fs containing a module with two functions for the UI to call, initializeStorage() and createGuestBookEntry().
Here is the final view of our three F# projects in Solution Explorer.
We’re left needing only a little code in the GuestBook_WebRole project. We add references to GuestBook_Data and GuestBook_WebApp projects. We also need to give GuestBook_WebRole a reference to FSharp.Core, since otherwise it won’t be available when the web role runs in the cloud. We delete Default.aspx and replace it with the five files from \Source\Ex1-BuildingYourFirstWindowsAzureApp\Assets, as instructed in steps 2 and 3 of Task 3. The only code we add to Default.aspx.cs is for managing the timer and delegating to the GuestBook_Controller the the job of initializing storage and creating guestbook entries.
Finally, we follow the manual’s instructions at the end of Task 3 for adding the DefaultDataConnection connection string and modifying Global.asax.cs. We can now test the app using the Verification instructions in the manual, and we’re done with Exercise 1.