4
votes

I'm working an ASP.net MVC cloud service project running two roles, a web role and a worker role. One of the pages in the web role initiate a request to build an APK file, building an APK file on the server can take anywhere from 1-5 minutes. So we came up with the following flow:

  1. The user initiate the APK building process on the page.
  2. The request is routed to our mvc action, creating a new message on an Azure Storage Queue.
  3. The Worker role is always polling from the queue and starts the APK building process. Now that the APK is ready we want ideally to notify the user by: (a) sending an email, which is working now. and (b) notifying the user on the page using SignalR.

Our problem is now in the SignalR part, how can we notify the user on the page that the APK is ready and he can download it.

1

1 Answers

1
votes

EDIT - Copying contents of the first comment for the sake of completeness -

I've looked the question again and I understand that you are using a worker role to poll the queue. In this case, you can make your work role a .Net SignalR client that connects to the APK signalR hub on the web role. The signlaR hub on the web role can simple forward any message it receives from the .Net client to the javascript client (browser).

I would recommend going through the below links

Hubs API Guide - Server
Hubs API Guide - Javascript Client

before going through rest of the answer.

As can be understood from the above two links, SignalR enables the server to 'push' data to the client. In order for this to happen, you require two things -

  1. A signalR hub - this is the 'hub' to which clients can subscribe to in order to receive messages.
  2. A client connected to the hub

Your signalR hub on the server can look something like this -

    public class APKHub : Hub
    {
        public async Task JoinGroup(string groupName)
        {
            await Groups.Add(Context.ConnectionId, groupName);
            Clients.Group(groupName).sendMessage(Context.User.Identity.Name + " joined.");
        }

        public Task LeaveGroup(string groupName)
        {
            return Groups.Remove(Context.ConnectionId, groupName);
        }

        public void NotifyUser(string userId)
        {
            this.Clients.Group(userId).notify();
        }
    }

On the client, your code might look something like this -

var notificationHandler = function () {

var url;
var user;

var init = function (notificationUrl, userId) {

    url = notificationUrl;
    user = userId;

    connectToAPKHub();
}

var connectToAPKHub = function () {
    $.connection.hub.url = url;
    var apk= $.connection.apkHub;

    apk.client.notifyUser = function (user) {
        console.log(user);            
    }

    apk.client.addMessage = function (message) {
        console.log(message);
    }

    $.connection.hub.start().done(function () {
        console.log('connected to apkhub');
        apk.server.joinGroup(user);
    })
}


return {
    init: init
}
}();

The notificationUrl is the URL that the signalR server is listening to.

This sets up your basic hub on the server and you should now be able to connect your client to the signalR hub. When the APK is built, you can use the following code (place it anywhere - for ex - in a controller action) to actually push a message to the concerned client -

var apkHub = GlobalHost.ConnectionManager.GetHubContext<APKHub>();
apkHub.Clients.Group(groupName).notifyUser(groupName);

The groupName can be an identifier that uniquely identifies a user.

Hope this helps.