Having the Server Update the Client with SignalR: A Case Study, Part I

In earlier posts, I described how to set up a SignalR server-side hub that can receive message from and, more importantly, send messages to the client, how to create a JavaScript client that could send and receive messages from that hub, and how to send and receive messages from  .NET (i.e. non-JavaScript) clients. As I noted in those articles, these are things that any Web Service can do.However, with an ordinary Web Service, the server can only send a message as part of response to a request from the client. SignalR lets the server send messages to the client whenever the server has information to share with the client and have the client receive the messages immediately (at least, with up-to-date browsers; with older browsers SignalR automatically falls back to repeatedly checking the server for updates (polling).

But all of those posts used simple “proof of technology” examples (I was only passing text between the clients and the server, for instance). This post will do something more realistic (not “realistic,” just “more realistic”) with the tools developed earlier. I’ll also introduce some SignalR functionality that makes it easy to send messages to groups of users.

A Sales Order Status Notification System

For my case study, I’ll assume a Web-based Ajax-enabled sales order system. While it would be possible to create all the Web Services required by this application (all the CRUD operations on sales orders, for instance) using SignalR for those operations doesn’t make a lot of sense. A SignalR hub requires a persistent connection between the client and the hub so that the hub can send messages to the clients at will (in fact, the technology that I’ve used in these posts is a wrapper for a more low-level object called PersistentConnection)–those connections have to start piling up at the server and should be used…judiciously. While I haven’t done any performance testing a hub certainly appears to be a singleton object which suggests that it could form a bottleneck within an application. And, besides, the CRUD operations don’t require what SignalR provides: immediate updates from the server when something changes at the server. SignalR shouldn’t be used indiscriminately but only where it adds value.

So where, in this application, would using SignalR add value? While most sales orders are processed in a cycle that takes several days, the application lets customers ask for “expedited processing” which causes the order to be placed and the product shipped (or a service team dispatched) in a few seconds–one or two minutes at the most. In this scenario, the user can leave their client up and see updates as the sales order progresses through the processing cycle.

To support that, the application register expedited orders with a SignalR hub. As applications elsewhere in the company process the sales orders they will send messages to the same hub that all the other clients have connected to. The hub will then update the interested client (or clients) in the sales order’s progress

Creating the Infrastructure

I’ll start by defining a class that I can use to pass sales order status around and an enumerated value for the various statuses that an order can go through:

Public Class SalesOrderStatusUpdate
    Public Property ID As String
    Public Property Status As SalesOrderStatus
End Class

Public Enum SalesOrderStatus
    Started
    InProcess
    FinalProcessing
    ProductOrServiceTeamDispatched
End Enum

My next step is to define the hub with a method that allows clients to tell the hub which sales order(s) the clients are interested in tracking. In this organization it’s possible that multiple clients may want to track a single order so, to support that, I’ll take advantage of SignalR’s Groups functionality. SignalR Groups allow you to create named collections of clients that you can send messages to (without sending messages to clients in other groups). Clients are added to groups by passing the group name (which you make up) and the client’s ConnectionId (which can be retrieved through the hub’s Context property).

This is the method that the client will call, passing a sales order Id, to register the client’s interest in a sales order. The code adds the client to a group with name of the sales order Id. The code then sends a return message to the caller the using the Clients Caller property (this code assumes that the client has defined a function called acknowledgeRegister to accept that message):

Imports System
Imports System.Web
Imports Microsoft.AspNet.SignalR

Public Class SalesOrderNotifications
    Inherits Hub

    Public Sub RegisterForSalesOrderStatusUpdate(ID As String)
        Me.Groups.Add(Me.Context.ConnectionId, ID)
        Me.Clients.Caller.AcknowledgeRegister("Registered")
    End Sub

The next step is to define a method that other applications can call to provide information about the sales order’s status. This method will accept two parameters (a sales order’s Id and the current status of the sales order), create a SalesOrderStatusUpdate object with those values, and then send that SalesOrderStatusUpdate object to all the members of the group with the same name as the sales order Id. Sending that message is especially easy: Just use the Clients’ Group method, passing the group name.

This code does all of that (and assumes that clients have implemented a method called GetSalesOrderStatusUpdate to receive the message):

Public Sub UpdateSalesOrderStatus(ID As String, Status As SalesOrderStatus)
    Dim sosu As New SalesOrderStatusUpdate With {.ID = ID, .Status = Status}
    Me.Clients.Group(ID).ReceiveSalesOrderStatusUpdate(sosu)
End Sub

Also, on the server, I need to register my SignalR hubs with this line of code (in an ASP.NET MVC application, I’d include this code with the rest of my routing commands; in an ASP.NET application, I would put it in my Global.asax file’s Application_Start method):

RouteTable.Routes.MapHubs()

Creating the Client

On the client, I need to open a connection to my server when the user requests the “Expedited Order” option. First I need a textbox to hold the sales order id and a checkbox to let the user indicate that the order is to be expedited:

<asp:TextBox ID="salesOrderIdTxt" runat="server" />
<asp:CheckBox ID="expeditedOrderChk" runat="server" Text="Expedite Order" />

When the checkbox’s change event fires I need to see if the box has been checked or unchecked. If the box has been checked, I will create a connection to my SalesOrderNotifications hub:

<script type="text/javascript">
 var conn;
$(function ()
 {
   $("#expeditedOrderChk").change(function ()
       {
         if (this.checked)
           {
            conn = $.connection.salesOrderNotifications;

The next step is to supply client-side JavaScript functions to be executed when the hub either calls the  acknowledgeRegister or receiveSalesOrderStatusUpdate methods at the server:

            conn.client.acknowledgeRegister = function (message)
                    {
                     $('#messages').append(message);
                    };
            conn.client.receiveSalesOrderStatusUpdate = function (sosu)
                    {
                      $('#messages').append(sosu.Id + ": " + sosu.status);
                    };

Now, when the hub calls the aknowledgeRegister method passing a string (in the server-side RegisterForSalesOrderStatusUpdate method) the JavaScript code here will add that string to the messages element on the page; similarly when the hub calls the recieveSalesOrderStatusUpdate method (as part of the hub’s UpdateSalesOrderStatus method) the Id and status properties will be pulled from the object and added to the messages element.

I’m now ready to start the connection. This code, after starting the connection, retrieves the sales order id from the page and passes it to the hub using the registerForSalesOrderStatusUpdate method to indicate that I want to receive messages about the sales order (i.e. to have me added to the group for this sales order Id):

            $.connection.hub.start().done(function ()
                    {
                     var soId = $("#salesOrderIdtxt").val();
                     conn.server.registerForSalesOrderStatusUpdate(soId);
                    });
           }
       });
 });

In my next post, I’ll look at the applications that will send sales order updates to the hub and allow clients to deregister from the hub.

Astute readers will recognize that what I’m implementing here is a variation on the Observer pattern. In the Observer pattern, an object maintains a list of clients that are to be notified when something happens. Clients register with the observer to be added (or removed) from this list. In the basic Observer pattern, all the clients are simply notified that “something has happened”  at the object. To find out what has actually happened, the clients have to request information from the object to find out exactly what has happened. In more sophisticated examples, the clients are sent information about the change in the object which may make it unnecessary for the client to request information from the object (either the clients get all the information they need or can use the information to determine that they’re not interested in the change). This is the way that, for instance, the .NET events system works, passing additional information in the e/EventArgs parameter. Other variations allow the clients to specify what information they’re interested in receiving and avoid being notified about all changes in the object. The Observer pattern (and many others) are described in Learning Tree’s 511 course, .NET Best Practices and Design Patterns.

Peter Vogel

Type to search blog.learningtree.com

Do you mean "" ?

Sorry, no results were found for your query.

Please check your spelling and try your search again.