As we continue down the path of looking at cross platform data options, let’s reminisce of where we came from…
- First we argued with a hipster about whether storing data in SQLite was good or not…
- Then we got down and dirty with some cold hard data caching…
- Finally we explored the awesomeness of Akavache…
Now our gaze turns towards the sky … if we can save data locally … and download data and store it for later use … why can’t we take our data and push it to the cloud?
We’re going to do that and a whole lot more!
Over the series of the next couple of posts we’re going to build out a fully functional application with Xamarin Forms that utilizes everything we learned from the previous posts on SQLite, caching and Akavache.
We’ll then combine all that knowledge with what we’ll learn in this set of upcoming posts on Azure Mobile Services and Storage. We’ll cover the basics of uploading data to and retrieving data from Azure, handling offline edits and synchronization, what to do when there are conflicts, and also how to load BLOBs to the cloud – something that wasn’t possible from a PCL based Xamarin app until just recently (introduced at Build 2015).
What kind of app are we going to build that needs to do all of that?
Well, I’m from Wisconsin … and there are only 3 things we think about in Wisconsin … the Packers, beer and cheese.
Don’t tell anybody, but I’m not a Packers fan, so no Packers app.
If we start doing something with beer, this blog will take an entirely different turn.
So that leaves cheese.
We’re going to build a cheese gathering and rating app. You sample a cheese, take notes on the complexities of its flavors, and then rate it comparatively to other cheeses … (yes, we take our cheese seriously here in the Dairy State). And we’re going to make it cross platform at that. Using Xamarin Forms, of course!
First though, let’s look at how we can upload data to & retrieve data from the cloud – with an overview of the API of Azure Mobile Services provides.
Azure Mobile Services
What exactly is Azure Mobile Services – or more to the point of what we’re talking about today – what does the data portion of Azure Mobile Services give us?
Essentially it’s a database with all the benefits of the cloud. Always on. Easily scalable. Robust. And maybe best of all, somebody else takes care of the day to day maintenance for us.
The database that backs the Azure Mobile Service is SQL Server and the tables we’ll setup within it are your regular run of the mill SQL Server tables, just with a couple of extra metadata columns automatically thrown in to help Azure manage concurrency, synchronization and deletes.
I won’t go through the details of setting up a Mobile Service here, there are plenty of tutorials.
One thing I do like to do however, is set the Mobile Service to use the javascript backend instead of the .Net backend. I’m a big fan of node.js, and this option allows us to use it.
And hey, look at this! I’m giving a talk at That Conference on node.js for .Net developers… August 11, 2015… cool! Everybody should go! (Yup…Shameless self promotion).
Azure Mobile Services also gives us a toolset, in the form of a PCL, to make interacting with this database in the cloud very easy. We’re going to spend the rest of this blog post looking at that API.
Getting Started With The Azure Mobile Services Library
Installing
You can get access to the Mobile Services API in 2 different ways, one is to install the component from the Xamarin Component Store, and the other is to download it from NuGet. I prefer the NuGet option, as that seems to be the most up to date.
Install-Package WindowsAzure.MobileServices
If you are using a PCL based Xamarin Forms application, make sure you also install the package into the platform projects as well as the common PCL project.
Once the library is installed into the projects, everything we’ll be talking about below is located in the Microsoft.WindowsAzure.MobileServices
namespace.
Modeling the Data
Since we’re talking about data here, we have to model it somehow, right? Azure mobile services works off plain old CLR objects … or POCO classes.
In order to automatically map to the table and column names in Azure, you need to name the class the same as the table name and the properties the same as the column names as shown below. (We can perform manual mapping, see the Attributes section below for more info).
The following is our cheese table:
Which maps to a class definition of:
public class Cheese
{
public string Id { get; set; }
public string CheeseName { get; set; }
public string CheeseType { get; set; }
public string Dairy { get; set; }
public int Rating { get; set; }
public DateTime ReviewDate { get; set; }
}
One thing to note is that you don’t have to provide all of the table’s columns in the class that you’re mapping to. You can leave some out without ill effect. However – you must have the Id column – that one is needed. It’s the primary key and used to perform updates and deletes as we’ll see below.
You may be wondering what’s with those columns that start with the underscore? Those are metadata data columns that Azure adds automatically for us. They are used to help Azure in data sync’ing and concurrency. A brief explanation of all the “metadata” columns Azure automatically inserts into each table follows:
Column Name | Description |
---|---|
id | The primary key of the table. |
__createdAt | A date field to indicate when the record was created in Azure. |
__updatedAt | A date field to indicate when the record was updated in Azure. |
__version | A string field (although it says timestamp) that changes every time a record is modified. This gives Azure the ability to tell if a record is out of date during synchronization of offline edits. |
__deleted | A boolean field to indicate whether a record has been soft deleted. A soft delete means the record will not be returned by queries from Azure, but it still exists, and can be resurrected if needed. |
Modeling the Data with Attributes
What if you already have an existing model class and you can’t change it, but still need to use it with Azure? No worries – we have a means to accomplish that as well. We just need to decorate our model class with some attributes to map the class and property names back to the table and column names.
The Mobile Services library gives us 5 attributes which perform some of the mappings for us. The other attributes we need come from our old friend, the JSON.Net library.
Behind the scenes, the Mobile Services library converts our POCO objects into JSON objects before it sends them up to the cloud, so it only stands to reason that we would use JsonProperty
attribute class to map class property names to table column names.
If there are any properties that we have defined in our class that we don’t want included in Azure, we use the [JsonIgnore]
attribute.
So, if we had a FrenchCheese
model class that had completely different property names we needed to map to our cheese table in Azure, and one property that we didn’t want to map at all, we would use the following:
[DataTable("cheese")]
public class FrenchCheese
{
[JsonProperty("Id")]
public string FromageId { get; set; }
[JsonProperty("CheeseName")]
public string NomDuFromage { get; set; }
[JsonProperty("CheeseType")]
public string TypeDeFromage { get; set; }
[JsonProperty("Dairy")]
public string Laitiers { get; set; }
[JsonProperty("Rating")]
public int Estimation { get; set; }
[JsonProperty("ReviewDate")]
public DateTime DateDeRevision { get; set; }
[JsonIgnore]
public string NePasInclure { get; set; }
}
As you can see, we used the JsonProperty
attribute class to map the properties over the table’s columns, passing in the name of the column as defined in Azure. [JsonIgnore]
to leave one out. And we also used the DataTable
attribute class to map the class name to the table’s name. DataTable
is provided by the Microsoft.WindowsAzure.MobileServices
namespace, which also contains the following attribute classes.
Attribute Class Name | Description |
---|---|
TableName(*string*) | Used on a class to map to the passed in Azure table name. |
CreatedAt | Maps a class's property to the __createdAt column in the Azure table. |
UpdatedAt | Maps a class's property to the __updatedAt column in the Azure table. |
Deleted | Maps a class's property to the __deleted column in the Azure table. |
Version | Maps a class's property to the __version column in the Azure table. |
Gotcha
One thing to note is that anytime we create a new property in our model class, Azure will want to create a new column in the backing table automatically when the object is inserted or updated in Azure. If that’s not your intention, make sure to decorate the property with the [JSONIgnore]
attribute class.
Gateway to the Clouds
When working with the Mobile Services library, everything runs through the MobileServiceClient
class. From that class we’re able to obtain table references which we’ll be able to perform both read and write operations.
Generally in the constructor of the MobileServiceClient
we’ll pass both the URL of our mobile service and its application key. We can also create a series of custom HttpMessageHandler
classes to intercept the requests and handle any error responses in a custom manner.
In order to “fire up” the Mobile Services library on the current platform you’re running on, be it Android, iOS, or Windows phone, the following line of code must be called from the platform specific project
Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init ();
Nothing tricky there – we’re just initializing the library to run on the current platform.
How it Works
Behind the scenes, whenever we try to modify data in or return data from Azure, the Mobile Services Library is sending HTTP requests to a REST service on our code’s behalf. We could communicate just as well with Azure by just issuing HTTP requests ourselves – but why would we when we have a library to wrap it all up for us, provide strong typing, and return decent exceptions?
But knowing that it’s just communicating via REST services is helpful when realizing that why we have to decorate some properties with Json attributes in order to get them to map properly.
Getting a Strong Reference to an Azure Table
Above we saw how to model the data to map to a table in Azure, but how exactly would we get a strong reference to that table? By using the MobileServiceClient.GetTable<t></t>
function.
Obviously here, <t></t>
is the type of the class you want to obtain a reference to. The return is an object that implements IMobileServiceTable
. It also has all of the properties of our model class – in other words… strongly typed.
From here we can proceed to perform CRUD operations…
Inserting, Updating and Deleting Data
Alright – let’s finally get down to it and talk about modifying some data in Azure shall we?
Just as in any data-centric library there are 3 operations we can perform: insert, update and delete. The table below lists the functions available from the library.
Function | Description |
---|---|
InsertAsync | Asynchronously insert the strongly typed object |
InsertAsync | Same as above, but also pass along key/value pairs in the query string which can be used on the server side for custom processing |
UpdateAsync | Same as above, except an update operation. Make sure the Id property in the class is present. |
UpdateAsync | Same as above, except an update operation |
DeleteAsync | Same as above, except a delete operation |
DeleteAsync | Same as above, except a delete operation |
UndeleteAsync | Using the passed in object, updated the __deleted column of the table to false, essentially undeleting the record, assuming soft deletes are enabled. Make sure the Id property in the class is present. |
UndeleteAsync | Same as above, but also pass along key/value pairs in the query string which can be used on the server side for custom processing |
One thing to note is that all of the above functions have 2 additional overloads – they can also take a JObject
instead of a strongly typed model class. I’m sure there’s a reason why that may come in useful – but for our purposes, we’re going to stick with the strongly typed version.
An example of creating a new cheese follows:
// Get an instance of the cheese!
var blueCheese = new Cheese {
CheeseName = "Little Boy Blue",
Dairy = "Hook's",
Rating = 5,
ReviewDate = DateTime.Now,
CheeseType = "Blue"
};
// Create the mobile service client
var mobileClient = new MobileServiceClient (@"https://your_url_goes_here.azure-mobile.net/",
@"your_app_key_goes_here");
// Grab a strongly typed table reference
var cheeseTable = mobileClient.GetTable<Cheese> ();
// Insert the cheese
await cheeseTable.InsertAsync(blueCheese).ConfigureAwait(false);
It is worth noting that these data modification operations execute immediately, so we need to provide a means to fallback on if the device does not have internet access.
Reading Data
What goes into the cloud must come back out. Let’s make it rain data!
We can get data out of Azure by using the IMobileServiceTable
and invoking ToListAsync()
which will return up to 50 records from the table.
Of course we probably wouldn’t ever just want to call that on the table itself to get 50 random records.
We can do much more interesting things by using the IMobileServiceTableQuery<T>
interface. To get a reference to that all we need to do is invoke a function on the table object such as the Where
function, which allows us to send in a predicate to filter the results that come back.
The following shows an example of using the where clause along with and order by:
var topCheeses = await cheeseTable.Where (c => c.Rating >= 4).OrderBy (r => r.Rating).ToListAsync();
It should be noted that we’ll always have to call ToListAsync()
or ToEnumerableAsync()
to actually invoke the query against Azure and get the records out.
By default, Azure will return 50 records at a time. To increase or decrease that amount, use the Take(int count)
function to return the specified number of rows.
If you are dealing with paging and need to skip over a certain amount of rows, the Skip(int count)
is for you. This will skip over the amount of rows specified, and then return the next 50 – or if combined with Take(int count)
, return that many.
For example, the following will skip the first 20 records of our highly rated cheeses, then return the next 10.
var topCheeses = await cheeseTable.Where(ch => ch.Rating >= 4).Skip(20).Take(10).ToListAsync();
Also to help with paging, there’s the IncludeTotalCount()
function which can be chained along with all the other functions mentioned above. By invoking this function, we’ll know how many rows exist in the database that match the given criteria of the query, thus we’ll know when to stop the paging. The following example shows how to get the total count out.
// allCheese will have 10 records
var topCheeses = await cheeseTable.Where(ch=> ch.Rating >= 4).Skip(20)
.Take(10).IncludeTotalCount().ToListAsync();
var totalCountEnumerable = topCheeses as IQueryResultEnumerable<Cheese>;
// total count will be 100+ in this case (we have a lot of great cheese in the database!)
var overallCountOfGreatCheese = totalCountEnumerable.TotalCount;
The IncludeDeleted()
function will return records that have been marked as soft deleted. This will be useful when you intend to invoke the UndeleteAsync<T object>
function described above.
There are several other functions that are apart of the IMobileServiceTableQuery<T>
interface that deal with ordering and projecting results onto anonymous types, but those are all analogous to the familiar Linq counterparts.
Design Considerations
Finally we get to some design considerations to take into account while creating applications that interface with Azure Mobile Services.
- I have found it’s best to keep the overall data model as flat as possible – meaning denormalized and not a lot of relational joins. This makes data retrieval and modification much easier on a per screen basis and it makes it easier to stay true to point number 2, which is…
- Try to have a single table provide all the data for a screen within the app. Again, this reduces the number of calls you’ll have to make to the cloud to retrieve all the data and minimize what you have to assemble on the device just to display a single screen. Same goes for when you need to push data to the cloud.
- This is a minor point, but ideally your model classes mirror the Azure table directly so you don’t have to decorate your classes with attributes – just to reduce potential confusion.
- Cache data and have it ready so the user can edit offline … which is the topic of our next post…
Summary
Azure Mobile Services provides us with a powerful mechanism to store data in the cloud with relatively minimal effort on our part. All we need to do is setup the site in Azure, download the NuGet package, then we’re off and running.
Mobile Services allows us to model the tables with POCO objects – and by using these POCO objects, we have strongly typed references to deal with our data instead of having to translate to and from Json objects.
Data modifications are all done in an asynchronous manner, using the normal routines as one would expect. Data retrieval has a number of options to provide the exact data we need without having to return too much – or too little – from the cloud.
Next up we’re going to dive into how to make changes to our data while the device is not connected to the internet, and then sync those changes back up to Azure once the device is connected again. That’s some powerful stuff…
Comments