Long Running Azure Mobile Service Call With Feedback using SignalR

I’ve been thinking a little more about alternative mechanisms for sending feedback to the client applications when a long running mobile service call is executing. In my previous post on Long Running Custom API Calls in Azure Mobile Service I discussed returning immediately to the client application whilst continuing to process a long running task. Unfortunately this means there is no way to provide feedback. In this post I’m going to add in SignalR to provide that real time communications link.

 

 

image

I’ll define a class that in herits from Hub and exposes a Services property – this will be populated automatically by the Mobile Service.

public class LongRunningFeedbackHub : Hub
{
    public ApiServices Services { get; set; }
}

Next I’ll amend the Get service method to a) take a parameter (which is the connection id of signalr on the client) and b) provide feedback during the long running task via the hub object. Note the use of the dynamic Progress method which will correlate to the client side proxy method that it subscribes to.

public async Task<string> Get(string id)
{
    var host = new ReportHost();
    host.DoWork(async (cancel) =>
    {
        try
        {
            var hub = Services.GetRealtime<LongRunningFeedbackHub>();

            var max = 5;
            for (int i = 0; i < max; i++)
            {
                await Task.Delay(TimeSpan.FromSeconds(5), cancel);

                hub.Clients.
                    Client(id)
                    .Progress(string.Format(“{0}% complete”,100*i/5));
            }
        }
        catch (Exception ex)
        {
            // Don’t bubble the exception – do something sensible here!
            Debug.WriteLine(ex.Message);
        }
    });
    Services.Log.Info(“Hello from custom controller!”);
    return “Hello”;
}

I added the initialization logic for the SignalR to the Register method of the WebApiConfig.cs file, as well as providing an explicit route to support the id parameter

config.Routes.MapHttpRoute(
    name: “DefaultApi”,
    routeTemplate: “api/{controller}/{action}/{id}”,
    defaults: new { action = RouteParameter.Optional, id = RouteParameter.Optional }
);

SignalRExtensionConfig.Initialize();

On the client side I add the SignalR .NET Client library from NuGet

image

And then add a ConnectToSignalR method to establish the hub connection and return a connectionId, which will then be passed into the view model via the GenerateReport method.

private async void GenerateReportClick(object sender, RoutedEventArgs e)
{
    var connectionId=await  ConnectToSignalR();
    CurrentViewModel.GenerateReport(connectionId);
}

private async Task<string> ConnectToSignalR()
{
    var hubConnection = new HubConnection(MainViewModel.MobileService.ApplicationUri.AbsoluteUri);
    //if (user != null)
    //{
    //    hubConnection.Headers[“x-zumo-auth”] = user.MobileServiceAuthenticationToken;
    //}
    //else
    //{
    hubConnection.Headers[“x-zumo-application”] = MainViewModel.MobileService.ApplicationKey;
    //}
    IHubProxy proxy = hubConnection.CreateHubProxy(“LongRunningFeedbackHub”);
    await hubConnection.Start();

    //string result = await proxy.Invoke<string>(“Send”, “Hello World!”);
    //var invokeDialog = new MessageDialog(result);
    //await invokeDialog.ShowAsync();

    proxy.On<string>(“Progress”,
        msg => Debug.WriteLine(msg));

    return hubConnection.ConnectionId;
}

The only change to the GenerateReport method is for it to accept an id parameter and for this parameter to be passed into the custom api

var result = await MobileService.InvokeApiAsync<string>(“Reporter”, HttpMethod.Get, new Dictionary<string, string>{{“id”,connectionId}});

When this is run and the GenerateReport method is invoked, the current percentage complete is passed back to the client and appears in the handler for the Progress message.

Leave a comment