asynchronousmodel-view-controllersignalriprogress

SignalR Progress through MVC Controller


Work Flow Scenario

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

  1. My controller calls service method

    string contents = await    _logService. ResponseFileWaiterAsync         (myData.guid.ToString());
    
  2. 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));
        }
    
  3. 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?


Solution

  • 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.

    1. Controller calls service method

      await  _logService.ResponseFileWaiterAsync(myData.guid.ToString(),clientId);
      
    2. 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

    1. MyResponseHub is empty. All I needed was to make sure the SignalR has a hub to work through its magic

                            public class JbaseResponseHub : Hub
                              {
                              }
      
      1. Client side JS

       $(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