Friday, December 28, 2012

SignalR and Interval Polling for Dashboard Update

Have you ever wondered how some dashboard update their content with new data without the users intervention? If you check out the google finance stock dashboard you see the stock data is updated every few seconds. Google finance can be found here http://www.google.com/finance
I can think of two ways of achieving this objective,
   A.    Interval polling
This approach involves the client polling the server for new data in preconfigured time interval.
The client displays the new data and waits for the specified time and repeats the request again.
The drawback of this alternative is the unnecessary requests that is wasted to continually check for new data- even if the new data is available or not.

The conversation between the client (browser) and the server goes along the line of
Client: “Do you have new Data?”
Server: “No”
Client waits for 5 seconds and tries again
Client: “Do you have new Data?”
Server: “yes”
Client will update the dashboard

However, instead of the client continually checking the server for new data, Is there a way where the server can notify the client when the new data is ready. Luckily yes. Here SignalR comes to the rescue.

 B.     SignalR
This library allows us to write an application in which there is a bi-directional communication between the server and the client. In the newest version of browsers that support the new HTML5 API web sockets are used for communication. In browser where web sockets are not supported it will fallback to other legacy options to achieve the same objective. The list of fallback options include long polling ,interval polling etc.
At the core of this implementation we have the  Connection Hubs . Hubs provide a high level RPC framework over PersistentConnection. All the notification from the server to the client and vice versa is done through the hub. When the application is run, SignalR will emit the javascript equivalent of the Hub you defined in your C# assembly. So that you can start a connection from the client and then call methods defined in the hub(Server Calls). Moreover you can add client call in your javascript file that can be called by the server methods for server to client communication.
The SignalR documentation can be found here
The SignalR Nuget package installation procedure can be found here 

Lets see some code …
1.       Install Nuget package . In this example I am hosting the hub inside asp.net mvc app.
2.       Register the routing for signalr hub to be accessed through URL . Note: the signalr route registration should come before the asp.net mvc route registration.
If you don’t specify an alternative path name. The default /signalr/hubs will be used
        protected void Application_Start()
        {
            RouteTable.Routes.MapHubs();
            RegisterRoutes(RouteTable.Routes);
           
        }



3.       Server Code : I created a hub class called ReportHub. All Hub classes should inherit from the base Hub Class.
The ReportHub class now will have access to the public properties defined in the HubClass
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR.Hubs;

namespace SignalRDemo
{
    public class ReportHub : Hub
    {
         public void NotifyClients()
        {
            //notify all other clients to update their data
            Clients.Others.updateReport1();
            Clients.Others.updateReport2();
        }
       
      
        public void BroadCastData1Available()
        {
            Clients.Others.updateReport1();
        }

        public void BroadCastData2Available()
        {
            Clients.Others.updateReport2();
        }

    }
}

The ReportHub has three server calls that can be invoked by clients.
Looking NotifyClients() in detail

      In this line of code Clients.Others.updateReport1();
      updateReport1 is a method defined in the client.

I am sending out the notification to all clients except for the one who made the server call. If you           want the notifier  itself to be notified too, You can use Clients.All instead of Clients.Others. Note updateReport1() is a method defined in the client. Upon notification the client will execute the updateReport1() method in the javascript.

Some notable properties of Hub.
a.       Clients -  list of clients connected to the hub. This property will give you access to
Clients.All – all clients
Clients.Others – all clients except the one calling the hub’s method
Clients.Caller – the called calling hub method.
b.      Context
c.       Groups

4.       Client Code :
Register the following scripts on your client/view
<script src="/Scripts/jquery.signalR-1.0.0-rc1.min.js" type="text/javascript"></script>
   <script src="@Url.Content("~/signalr/hubs")" type="text/javascript"></script>
               
 "~/signalr/hubs” – this is the path defined on your route. When the application is run, the signalR assemblies will use reflection to emit the javascript equivalent of the ReportHub class defined in the assembly.
The hub can be accessed from the client as show below. The connections can be started and an optional call back can be invoked when the connection is started or done.
You can also define methods on the clients to be invoked by the server. The updateReport() method discussed in the above section (Server Code) is define as follows.
        // Declare a function on the myHub hub so the server can invoke it         
        rptHub.client.updateReport1 = function () {
            // do something
        };

       

The whole script of the client is shown below.
$(function () {
        // Proxy created on the fly         
        var rptHub = $.connection.reportHub;

        // Declare a function on the rptHub hub so the server can invoke it         
        rptHub.client.updateReport1 = function () {
            RefreshData('map_canvas');
        };

        rptHub.client.updateReport2 = function () {
            RefreshData('map_canvas2');
        };

        // Start the connection
        $.connection.hub.start().done(function () {
          
        });
     
    });
How can we call the server from the client?You will call the server property of the hub and then you will have access to all the public methods in the hub.
E.g. If you want to invoke the notifyClients() method defined in the server/hub.
    // Call the notifyClients method on the server
       rptHub.server.notifyClients();

 Putting it all together
I have included a demo asp.net mvc application hosting a signalR hub. The application has a dashboard page which has two geomap charts  and there is an admin page with three buttons. Clicking the buttons on the admin page will notify the charts on the dashboard to update their content with a new data. The demo shows the bi-directional communication from the client to the server and vice versa.
 Admin page to Server Hub => client to server
Server Hub to Dashboard page => server to client
The full demo can be downloaded from here