Above image represents the scenario I am trying to solve.
I know from my previous experience that the Progress reporting pattern can be used to solve the problem of keeping the client updated but back then client was communicating to the hub directly, meaning there was no controller or code behind.
In the above image, the problem is that controller needs to do stuff before it can make call to the DB.
Also, the DB returns segments in the form of files, so I added an async filesystemwatcher for notifying me every time a response file gets created.
The goal is to keep feeding client browser with response segments as when you receive it, until we reach the end of responses.
The question is: how do I trigger the signalR Hub from controller / service methods and keep feeding responses to client browser?
UPDATE:
To narrow down the problem
My controller calls service method
string contents = await _logService. ResponseFileWaiterAsync (myData.guid.ToString());
In the ResponseFilewaiterAsync
method, when the first file gets created I trigger the hub
BroadcastResponseToClient(responseFilePath, hubConnection);
private void BroadcastResponseToClient(string responseFilePath, HubConnection hubConnection)
{
var hub = hubConnection.CreateHubProxy(typeof(string).Name);
hubConnection
.Start()
.ContinueWith(_ => hub.Invoke("UpdateResponseToClient", ResponseHtml));
}
The hub reports the responses to client as well as go look for next file until it is done
public async Task UpdateResponseToClient(IProgress<string> progress,string filePath)
{
// Clients.Caller.appendReportToPage(ResponseHtml);
string result = await _jbaseLoggingService.ResponseFileWaiterAsync(filePath);
progress.Report(result);
}
Problem: how to trigger a progress report hub from service method / controller? Where should I complete my request when it is done with updates – should it go back to the controller?
I think I may have solved my problem. First thing I had to do was to render a empty placeholder client, so that I could start my client side connection (hub connection) and then make a AJAX call to the actual controller method while sending in connection id to the controller.
Controller calls service method
await _logService.ResponseFileWaiterAsync(myData.guid.ToString(),clientId);
Service waits for filewatcher to notify about file created/renamed then reads the contents of the file and communicates it back to client with the help of clientId
ResponseFileWaiterAsync(string responseFileName, string clientId)
{
//do stuff
await DropFileHelper.WaitForResponseFileAsync(responseFilePath);
contents = LoadResponseFile(responseFilePath);
//do stuff
Clients.Client(clientId).appendReportToPage(contents);
}
Clients are loaded in the constructor using
Clients = GlobalHost.ConnectionManager.GetHubContext<MyResponseHub>().Clients;
appendReportToPage - is the client callback method
MyResponseHub is empty. All I needed was to make sure the SignalR has a hub to work through its magic
public class JbaseResponseHub : Hub
{
}
$(function () {
var _clientId;
var hubProxy = $.connection.myResponseHub;
hubProxy.client.appendReportToPage = function (ResponseHtml) {
// Add the message to the page.
$('#responseBody').append( "<p>"+ResponseHtml+"</p>" + '</br></br></br>');
};
$.connection.hub.start()
.done(function () {
//alert('in hub start');
_clientId = $.connection.hub.id;
console.log('Now connected, connection ID=' + $.connection.hub.id);
//alert(_clientId);
$.ajax({
type: 'GET',
url: '/mycontroller/ProcessRequest',
data: { qs: $('#encryptedString').html().trim(), clientId: _clientId },
cache: false,
success: function (result) {
}
});
})
.fail(function(){ console.log('Could not Connect!'); });
});
Lessons learnt
Helpful links
http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client#genproxy http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr-and-mvc