Monday, October 1, 2012

Using Mobile Services in Windows 8 Managed Application (C# code)


Windows Azure Mobiles Services (WAMS) targets Windows 8 Applications in its first preview - a great starting point on the way to supporting multiple mobile platforms down the road. This post covers a few basics of using WAMS in managed code (C# or VB.NET) using C# examples. You could use VB.NET just as well. Some of this is already covered in tutorials but you might find this useful as a cheat sheet.

A Mobile Service created using WAMS has an http REST endpoint that can be used directly if you like. But  we  also ship an SDK (currently in MSI form) that elevates the experience quite a bit and bring it closer to the host language and platform. This post is about the SDK experience. If you haven't already completed the quick start and related tutorials, I strongly recommend you do those first. This post is intended to be complementary though I do repeat a few things to make the post a bit more self-contained.

Client

The main class is the MobileServicesClient - the go to class for all your needs. It takes two parameters:
  1. URL for the Mobile Service. This is globally unique (hence you need to pick a unique service name when you create one)
  2. App key: this identifies the app (since this is distributed with the app and viewable in fiddler or other browser dev tools, you could call it an "open secret"; i.e. it has identification value and no real security value)

public static MobileServiceClient client = new MobileServiceClient(appURL, appKey);

You start by getting a table proxy so that subsequent code can be strongly typed.

IMobileServiceTable<TodoItem> TodoTable = App.client.GetTable<TodoItem>();

Don't worry, just as in other LINQ incarnations, this table is just a proxy that knows how to perform CRUD type operations. The following statement does NOT retrieve the entire table down to the client. (In fact, for the app's good, we don't allow that even otherwise but more about it later).

The client also supports login in addition to CRUD (Create, Read, Update and Delete) operations.

Queries

In managed code, the query support is quite straightforward. It is basically the LINQ experience with support for a subset that roughly covers filtering, sorting and paging against a single collection (i.e. no joins, no groupby etc.).

Getting Results

There are a few options to get the results of a query that is executed asynchronously (hint: you can't just use regular ToList here; you need an awaitable task or something that can handle async ops)

// If you just want and IEnumerable. Returns an awaitable Task
var items0 = todoTable.ToEnumerableAsync();
// Friendly collection to iterate through
var items1 = todoTable.ToListAsync();
// More fancy data binding.
// ICollectionView, INotifyCollectionChanged, INotifyPropertyChanged and a bunch of other interfaces are implemented
MobileServiceCollectionView<TodoItem> items2 = todoTable.ToCollectionView();

For quick and dirty prototyping, I prefer to use MobileServiceCollectionView (MSCV). For more specific use; it is better to have your own custom implementation of ICV etc.

Filtering

MobileServiceTableQuery<TodoItem> query = todoTable.Where(todoItem => todoItem.Complete == false);
You could use the comprehension syntax if you prefer. It is just LINQ, though within limits of supported operators and member functions.
Lookup helps you get a single object.
// Singleton or null
var item0 = await todoTable.LookupAsync(id);
if (item0 != null)
{
    // For illustrative purposes only
    MessageDialog md = new MessageDialog(item0.Text);
    var ignoreAsyncResult = md.ShowAsync();
}

Sorting

As in case of filtering, you can use the standard LINQ operators with lambda or comprehension syntax:
var query = todoTable.OrderBy(t => t.Text);

In addition, you can also use other operators like ThenBy, OrderByDescending etc.

Paging

For mobile apps, data is often bound to a UI so it is often best retrieved a page at a time. On slower networks, even data retrieved for other purposes (i.e. not for directly displaying in a UI) should be limited through paging. Paging relies on LINQ Skip and Take operators.
// First page
var query = todoTable.OrderBy(t => t.Text).Take(page_size);
// For (n+1)th page
var query = todoTable.OrderBy(t => t.Text).Skip(n*page_size).Take(page_size);

Mobile Services runtime (server-side) provides paging by default by restricting the results to 50 objects. This is usually appropriate for most UI binding scenarios. You can override that by applying Take(n) where n can be any number from 1 to 1000.

There is also a way to get the total count of all objects matching your query (not just the first page). I will cover that in a separate post later.

CUD
Create, Update and Delete operations are quite straightforward.

    // Insert a new record
    TodoItem newItem = new TodoItem { Text = "Complete C# Client blog post", Complete = false };
    await todoTable.InsertAsync(newItem);

    // Update a record
    newItem.Complete = true;
    await todoTable.UpdateAsync(newItem);

    // Delete a record
    await todoTable.DeleteAsync(newItem);

Insert operation performs dynamic schematization. You don't need to go through the ceremony of creating a table with typed columns etc. Mobile Services run-time looks at the properties in the JSON payload and adds eponymous column(s) if they are not already present. It looks at the value of the property to infer the type. Of course, because the base types in JavaScript (which is what is used for server scripts and underlies JSON) are fairly simple, the list of types is much smaller than that in .NET or SQL. Here is a quick table that describes the types currently used.

JSON Value
T-SQL Type
Numeric values (integer, decimal, floating point)
Float(53)
Boolean
Bit
DateTime
DateTimeOffset(3)
String
Nvarchar(max)

Likewise, update operation can also perform dynamic schematization if necessary.

Typically you want dynamic schematization when you are developing your app for rapid turnaround and changes. When you deploy, it is usually a good idea to "lock down" the schema by turning off dynamic schematization so users of your app can't accidentally or maliciously expand the schema. Mobile Service developers can turn off dynamic schematization in the portal ("CONFIGURE" tab).

For the delete operation, the only property on newItem object that matters is the ID assigned by Mobile Service when the object was created in the table.

Untyped Usage
The C# client is primarily designed for strongly typed scenarios. However, sometimes a more loosely typed experience is convenient (e.g. objects with open schema). That is enabled as follows. In queries, you lose LINQ and you have to dropped down to effectively the wire-format.

    // Get an untyped table reference
    IMobileServiceTable untypedTodoTable = App.MobileService.GetTable("TodoItems");

    // Lookup untyped data using odata
    IJsonValue untypedItems = await untypedTodoTable.ReadAsync("$filter=complete eq 0&$orderby=text");

You get back JSON value that you can use like a property bag.

Login
I strongly recommend going through the tutorial on our dev center to understand authentication and users. This is just for completing the cheat sheet:
    // Log in
    MobileServiceUser user = await App.MobileService.LoginAsync(windowsLiveToken);