Getting to Know Azure Mobile App Cont.

Microsoft Azure Mobile App has recently gone GA (General Availability) and has definitely captured my attention. Mobile App is a tremendous accelerator that enables us to go from an idea to a functional prototype quickly. Then, we can continue to build on that initial investment to create a robust production ready app. Finally, this post is all about using Visual Studio Team Services (VSTS) to build and publish apps to HockeyApp, so that we can test and assess quality before our apps make it to our favorite app Stores.

So far, we’re using Template10 to build a Universal Windows Platform (UWP) Mobile App and we’re using Microsoft Account as an Identity Provider (IDP). This all got built using Visual Studio Team Services (VSTS) and distributed to testers using HockeyApp.

Now that we’ve got the basics out of the way, let’s build something.

Working with Data

As previously mentioned, Azure Mobile App is an accelerator and the goal of this post is to walk through the pieces that allow us to Create, Read, Update and soft Delete (a.k.a. CRUD) data from an Azure SQL Database.

The first thing we need is a model, that can be used to drive a User Interface (UI). The following is a Resource. It represents a link to something. It has a Title, some metadata and is also augmented with a command. This doesn’t look like a Data Transfer Object (DTO). It’s a Model.

public class Resource
{
    readonly Lazy<BrowseResourceCommand> browseCommand;

    public Resource()
    {
        browseCommand = new Lazy<BrowseResourceCommand>(() => new BrowseResourceCommand(Uri));
    }

    public ICommand BrowseCommand => browseCommand.Value;

    public string Id { get; set; }

    [JsonProperty(PropertyName = "uri")]
    public string Uri { get; set; }

    [JsonProperty(PropertyName = "title")]
    public string Title { get; set; }

    [JsonProperty(PropertyName = "createdAt")]
    public DateTimeOffset CreatedAt { get; set; }

    [JsonProperty(PropertyName = "updatedAt")]
    public DateTimeOffset UpdatedAt { get; set; }

    [JsonProperty(PropertyName = "shareFactor")]
    public double ShareFactor { get; set; }

    [JsonProperty(PropertyName = "interestFactor")]
    public double InterestFactor { get; set; }

    [JsonProperty(PropertyName = "proposed")]
    public bool Proposed { get; set; }
}

The browse command has a single goal, browse to the resource link in a browser. And we can bind it to a button on the UI.

public class BrowseResourceCommand : ICommand
{
    private readonly string uri;

    public BrowseResourceCommand(string uri)
    {
        this.uri = uri;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public async void Execute(object parameter)
    {
        await Launcher.LaunchUriAsync(new Uri(uri));
    }

    public event EventHandler CanExecuteChanged;
}

Ok, so we have a Model, we have a Command, and we need some data. Let’s start by creating a Resource.

var resource = new Resource
{
    Uri = "https://alexandrebrisebois.wordpress.com",
    Title = "Alexandre Brisebois' Blog"
};

Good, let’s persist this new Resource through our Mobile App backend. By getting a reference to the Resource Table and by calling the InsertAsync method, we handoff the hard work to the server-side OData APIs.

var table = App.MobileService.GetTable<Resource>();
await table.InsertAsync(resource);

Server-side things get interesting, because there isn’t much for us to do to get to a functional state. By leveraging the TableController we can hit the ground running and concentrate on our app.

To secure my API, I use the Authorize attribute and use the ClaimsPrincipal to get the nameidentifier claim. This is an important claim, because it is used by Mobile App to register authenticated users to a Push Notification Channel. For now, let’s store the claim value so that we can use it in my next post that will explore Push Notification in detail.

[Authorize]
public class ResourceController : TableController<Resource>
{
    private readonly ClaimsPrincipal claimsPrincipal;
    private readonly string nameIdentifier;

    public ResourceController()
    {
        claimsPrincipal = (ClaimsPrincipal)User;
        nameIdentifier = claimsPrincipal.NameIdentifier();
    }

    protected override void Initialize(HttpControllerContext controllerContext)
    {
        base.Initialize(controllerContext);
        MobileServiceContext context = new MobileServiceContext();
        DomainManager = new EntityDomainManager<Resource>(context, Request);
    }

    // GET tables/Resource
    public IQueryable<Resource> GetAllResources()
    {
        return Query();
    }

    // GET tables/Resource/48D68C86-6EA6-4C25-AA33-223FC9A27959
    public SingleResult<Resource> GetResource(string id)
    {
        return Lookup(id);
    }

    // PATCH tables/Resource/48D68C86-6EA6-4C25-AA33-223FC9A27959
    public Task<Resource> PatchResource(string id, Delta<Resource> patch)
    {
        return UpdateAsync(id, patch);
    }

    // DELETE tables/Resource/48D68C86-6EA6-4C25-AA33-223FC9A27959
    public Task DeleteResource(string id)
    {
        return DeleteAsync(id);
    }

    // POST tables/Resource
    public async Task<IHttpActionResult> PostResource(Resource item)
    {
        item.Proposed = true;
        item.PNID = nameIdentifier;
        var current = await InsertAsync(item);

        return CreatedAtRoute("Tables", new { id = current.Id }, current);
    }
}

To keep my life simple, I decided to apply the DRY (a.k.a Don’t Repeat Yourself) principle, by creating an extension method to encapsulate the extraction of the nameidentifier claim from the ClaimsPrinciple. I have a feeling that I will be using this a lot in the future.

public static class ClaimsPrincipalExt
{
    public static string NameIdentifier(this ClaimsPrincipal claimsPrincipal)
    {
        var claim = claimsPrincipal.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
        return claim == null ? string.Empty : claim.Value;
    }
}

Finally, the entity that is mapped back to the Azure SQL Database is greatly simplified by the use of the EntityData base class. It encapsulates everything that is needed for offline sync and soft deletes.

In this example, I used the JsonProperty attribute to dictate Json serialization, and I used the Column attribute for data that shouldn’t be returned to clients. This is definitely important if you need to keep some stuff private.

Data Locality is significant in some scenarios. Keeping data spread across many relational tables can often cause challenges when it comes to performance. When we’re dealing with a read optimized schema, denomalization is our friend. This technique helps us in this regard and can prove to be quite useful.

public class Resource : EntityData
{
    [JsonProperty(PropertyName = "uri")]
    public string Uri { get; set; }

    [JsonProperty(PropertyName = "title")]
    public string Title { get; set; }

    [JsonProperty(PropertyName = "shareFactor")]
    public double ShareFactor { get; set; }

    [JsonProperty(PropertyName = "interestFactor")]
    public double InterestFactor { get; set; }

    [JsonProperty(PropertyName = "proposed")]
    public bool Proposed { get; set; }

    [Column("PNID")]
    public string PNID { get; set; }
}

Jumping back to the App, reading data from the server is very similar to how we persisted data. We start by getting a reference to the Resource Table and compose a Linq query. This query then gets translated to OData and executed against the Azure SQL Database by our API.

public class SearchResourcesCommand : ICommandAsync
{
    public async Task Execute(object parameter)
    {
        var viewModel = (MainPageViewModel)parameter;
            
        Views.Shell.SetBusy(true, MessageService.GetSearchBusyMessage());
            
        try
        {
            var table = App.MobileService.GetTable<Resource>();

            // if the user let the search box empty, grab the top 50 new resources
            if (string.IsNullOrWhiteSpace(viewModel.Value))
            {
                viewModel.Resources = await table
                    .OrderByDescending(x => x.CreatedAt)
                    .Where(r => !r.Proposed)
                    .Take(30)
                    .ToListAsync();
            }
            else
            { 
                var split = viewModel.Value.Split(' ', ',', '.', ':', '\t');

                var reuslts = await split.Where(x => !string.IsNullOrWhiteSpace(x))
                    .Aggregate(table.CreateQuery(), (current, s) => current.Where(r => r.Title.Contains(s)))
                    .Where(r => !r.Proposed)
                    .OrderByDescending(r => r.CreatedAt)
                    .Take(50)
                    .ToListAsync();
            
                viewModel.Resources = reuslts;
            }
        }
        catch (Exception ex)
        {
            viewModel.WelcomeMessage = "Let's try that again";
        }
        finally
        {
            Views.Shell.SetBusy(false);
        }
    }
}

Share your thoughts in the comments below

In future posts, I will continue to implements various aspects of the Ask Alex Universal Windows Platform (UWP) Mobile App.

Trackbacks and Pingbacks:

  1. Dew Drop – May 4, 2016 (#2243) | Morning Dew - May 4, 2016

    […] Getting to Know #Azure Mobile App Part 4 (Alexandre Brisebois) […]

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.