Selective Updates with ASP.NET SignalR

SignalR is great for all those tasks that one would otherwise need to rely on AJAX. However, it is much more versatile than this: it allows, for example, a server process to update users' browser windows selectively rather than broadcast to all, and can treat groups of users in different ways. Dino explains how this magic works.

ASP.NET SignalR is an extremely powerful technology that, amongst its many uses, makes it easy for developers to update client pages from within Ajax requests. The pattern is fairly simple and natural. The client page places an Ajax request and then the server-side code performs the requested task. At the end of the task, or periodically during the ongoing task, the server-side code will call back some JavaScript client code and pass the results of the operation or any intermediate results achieved. Although updates can be achieved without SignalR, it provides a fluid programming model and a very robust connection between browser and host, based on the WebSocket protocol or using long polling if the WebSocket protocol is not supported by the browser. When using SignalR, the developer merely calls some server-side code that magically invokes a matching JavaScript method on the client. From a developer’s perspective it is like saying “here’s the current data, now let the browser know about it.”

Most examples out there just show only this most obvious use of the framework. It is certainly important to know about it: Personally, I’ve been using ASP.NET SignalR to display live scores on the browser and, more generally, where you just need to make server-side changes to whatever client browser appears to be connected.

However there is more to SignalR than providing Grown-up AJAX. In this article, I’ll talk about how you can use ASP.NET SignalR to send updates only to specific clients and not generically to all of them. You’d need to do this when you have a web site that performs a bunch of significant operations behind a login gate. In this case, updates must only be sent to the requesting client, or possibly a group of them, but not all of them indiscriminately.

Refreshing the ASP.NET SignalR Infrastructure

Before we get into detail, let’s briefly touch on the basics of the ASP.NET SignalR architecture. The API is made of two parts, one running on the client side and one running on the ASP.NET server side. The client API is not limited to JavaScript client browser usage, but is also supported from within WPF and mobile applications. In the client, you need to reference the core API (a jQuery plugin when using a browser) as well as the business API. The business API is essentially a JavaScript proxy API for the server-side API through which the server commands client refreshes. On the server, you define the business API-namely, the list of remote procedure calls you want to push to the client-through a class named the hub. The system then automatically generates a JavaScript proxy for this hub API.

You reference the hub proxy through a fixed JavaScript endpoint whose URL is /signalr/hubs. That would inject into the client DOM just the JavaScript code necessary to receive push notifications from the server. The big picture of ASP.NET SignalR is summarized in Figure 1.

2265-a7f95b5f-fd1c-4fb6-8fe4-2398b72ecde

As the name suggests, the big picture just provides an overall view of the architecture and hides a lot of important details. One detail that deserves attention is the connection being established between an individual client and the server-side hub. This is visible in Figure 2.

2265-a0ca54b5-a3d5-4798-9c39-1f0bc36a0c6

Each and every time that ASP.NET SignalR establishes a connection between a client and the server-side hub, it uniquely names the instance of the persistent connection. It generally uses a GUID. Should the server code need to access the underlying connection object, it can use the auto-generated connection ID which is programmatically exposed via the ConnectionId property on the hub context.

Even though the connection ID can provide a unique identity for a given connection, it is not much use as a means to identify the user behind that connection. To map an application user to an ASP.NET SignalR connection you, as a developer, need to create your own conversion table. Put another way, you need to create and maintain a dictionary where the key is the connection ID and the value is either the user name or any other piece of information that your application may use to track down users.

The ASP.NET SignalR infrastructure includes a component called the user ID provider that assigns a unique name to a connection. This name attempts to be more significant and meaningful than a raw GUID. This user ID provider is abstracted by the following interface:

ASP.NET SignalR provides a default implementation of the IUserIdProvider interface that is fully expressed by the following pseudocode:

In other words, SignalR automatically carries the name of the currently logged user and maps that to a pending connection. In those applications where any form of ASP.NET authentication is used, this method works effectively and the following expression allows us to retrieve the name of the logged user.

This infrastructure covers the vast majority of possible cases. Most of the time, in fact you just need to get a reference to the logged user that made a given request, and selectively update just that client browser. The user name is sufficient to retrieve just the matching connection. However, it may not be sufficient if the same user could have connected to the application through multiple instances of the browser, typically from several devices, and you intend to update all that user’s active connections. In this case, you may need to use a different identifier that better helps to group all connections made by each user. For this purpose, you can create and register a new user ID provider to map the matching between connections, devices and actual users. Let’s examine a few examples where this is required.

Updating Clients Based on User Name

Users log in and then start navigating into the section of the site whilst authenticated. From the landing page after authentication, users can click to reach another page where they can edit their own profile. The template used by all pages subject to authentication displays some user-specific information such as photo and age. You want that information to be refreshed every time a user changes anything through the profile page. The problem is that you only want to refresh all active pages that refer to those profile details of that particular user. If that the user is connected at the same time through multiple sessions perhaps across several devices, then the task becomes more complicated.

Generally speaking, there are two strategies to achieve this. You can push notifications to just a specific collection of clients or you can push to all of them and let the client-side code decide whether to run updates or not. The former option is recommended as it minimizes the amount of data transferred across the wire.

To identify a specific client, and the underlying connection, you use the following code.

The idea is that you select all clients with a matching user ID and push data back for some client side code to refresh the user interface.

This approach works beautifully for most pages behind the authentication wall. However, if you need to update only some of the connected pages but the authentication status is not sufficient to determine which pages should be updated, then you can consider pushing notifications back to all clients and let the client side code decide intelligently what to do. It goes without saying that, in this case, it is assumed that the client code knows how to determine whether the update is meaningful or can be ignored.

Passing a Custom User Name upon Connection

Most of the examples you find of ASP.NET SignalR will show how to start a connection and then how to receive notifications back on the client. The notification back consists in some server code in the hub object passing a response to the hub proxy (see Figure 1 and Figure 2) which in turn invokes some client code-JavaScript code when the client is a web client.

The interesting thing is that you can also invoke some hub code directly from the client code. For example, you can do that to pass a custom string upon connection. In this way, you can control the connection name of each and every instance of the client code that connects to the server. Here’s how to call a method on the hub upon connection.

The RegisterMeAs method is expected to be a method defined on the server hub class that in this case receives a unique name determined on the client and uses that in the most appropriate way to map users to connections.

More generally, however, the trick of calling a server hub method upon connection is just yet another way that you can build the programming interface for the feature you intend to have.

Using a Custom User ID Provider

Going through a hub method is a useful trick that leverages a mechanism devised to be a more general tool. If you merely wish to use some custom logic to name and identify a connected user (in all cases in which the authenticated user name doesn’t work), then the official and recommended solution is to use a custom user ID provider.

I’ve already mentioned that a user ID provider is a class that implements the IUserIdProvider interface. Here’s an example excerpted from a real application that I worked on recently.

The logged user is wrapped up in a custom IPrincipal object that exposes an instance of the User object as defined in the application. The User object has a property called InternalId which is nothing more than a database-generated progressive number created every time a new record is appended to the table. The InternalId value is essentially a primary key value for the table of users.

Creating a new user ID provider is only the first step. You then need to instruct the system to use the custom ID provider in lieu of the standard one. You do that in the startup code of the application right before setting up the whole ASP.NET SignalR thing.

The net effect of this code is that ASP.NET SignalR will use your own code to name each user behind each connection and won’t use the logged user name anymore.

This leads to a core question: why create a custom user ID provider if the default user ID provider already uses the name of the currently logged user? The point is that the name of the currently logged user is the name identified via the ASP.NET authentication process. When the user may connect to the application (therefore, not necessarily the web site) through several devices and client applications simultaneously, the name that identifies the user may or may not be the same as the one identified during the ASP.NET authentication. If you need to unify the token that identifies a given user through multiple devices, the best option you have is to use a custom ID provider.

A Word about Groups

ASP.NET SignalR also supports groups of clients for occasions where you really want to group together a few clients and just push notifications back to all of them simultaneously. The task of creating and managing groups is quite simple and, for the most part, transparent. There are no restrictions on the number of clients in a group and a client can be a member of more than one groups. Furthermore, groups don’t need to be created explicitly. A new group is created each time you add a new client to it. The same group is automatically deleted when there are no more connected clients and recreated on next subsequent addition of a client. The programming interface to add and remove clients to and from a group looks like below.

The Groups property is exposed out of the hub context class. To invoke a client method on all clients in a group, you just do the following:

You can even chain the “add” and “invoke” operations in a single method call.

Summary

Many developers get the misleading perception that ASP.NET SignalR merely helps to build a chat application or provides live updates of news, stock and score tickers. In reality, ASP.NET SignalR has a broader scope. You can use SignalR to refresh segments of the user interface once some Ajax operations have completed. Each client view that intends to be updated via SignalR registers with a hub and you can have as many hubs as you like on the server. However, not all devices connecting to a given hub from the same client proxy need to receive the same updates. In this article, I covered a few techniques to make selective SignalR updates to page behind an ASP.NET authentication layer, but there is a good chance that once you’re aware of the potential of SignalR, you’ll find many other uses for it in your applications.