I’
ve been looking into SignalR as an alternative to ajax as a means of keeping users apprised of the progress of long running processes.

My current project involves processing data that’s provided in a spreadsheet. I have an MVC app to which a spreadsheet is uploaded. The spreadsheet is validated for proper format (correct columns) and data (all values in the date columns are dates, values in other columns pass various validation rules). If the validation passes, data from the spreadsheet is forwarded on to another system.

There’s no database involved for this project, and it all happens on the web server. However, validation of the spreadsheet can take some time (more than 5 minutes) and I wanted to let the user know what was going on before they received a “pass” (file uploaded) or a “fail” (list of validations failed).

If there were a database, I might be tempted to do something with ajax. For example, storing data from the spreadsheet in a database then passing back a list of rows and having the client request validation for each row via ajax call. The ajax call could then return validation failures for that row, and the client could be updated.

But no database.

Also no problem. SignalR to the rescue.

As I’ve not used SignalR much prior to this project, there was some research involved. The biggest roadblock was trying to find a way to have SignalR send messages specifically to the browser instance that submitted the spreadsheet. Many SignalR examples involve broadcasting to multiple clients using “Clients.All”.

I found that the connection id identifying a browser instance to SignalR is available to both the client (javascript) and server (C#), so my solution was to pass this connection id along with the uploaded spreadsheet file.

Javascript


var _connection = $.hubConnection("http://localhost:60667/");
var _signalTestHubProxy = _connection.createHubProxy('LongRunningHub');;

var _thisClientConnectionId = null;

//Registers a handler that will be invoked when the hub method with the specified method name is invoked.
_signalTestHubProxy.on("addNewMessageToPage", ReceiveMessage);

_connection.start().done(function () {
    console.log('Now connected, connection ID=' + _connection.id);
    // Wire up Send button to call sendmessage on the server.
    _thisClientConnectionId = _connection.id;

})
 .fail(function () { console.log('Could not connect'); });

MVC Controller Action


[HttpPost]
public ActionResult SignalTestPost(Models.SignalTestPostModel model)
{
    string myContextId = model.ConnectionId;
    string myMessage = model.Message; 

    Result result = new Result() { Message = "All done, folks!" };

    var hub = GlobalHost.ConnectionManager.GetHubContext();
 
    string message = "all done, with extra signal r";

    hub.Clients.Client(myContextId).addNewMessageToPage("preparing to send message");

    hub.Clients.Client(myContextId).addNewMessageToPage(message);

    hub.Clients.Client(myContextId).addNewMessageToPage("completed sending message");

    return Json(result);
}