When “Pull” is Insufficient
Software developers live in a pull first world. Even modern single page applications (SPAs), while highly interactive and responsive, only respond to change when the client initiates it. A user sees a list of work orders, and the list doesn’t update unless that person requests an update, either by refreshing the page, or performing a change of their own that initiates a full or partial reload of data from the server. Often, this is an insignificant delay, but increasingly users are expecting a more real-time experience. We live by push notifications on our phones, for everything from social media to banking alerts. In line-of-business applications, transactions are becoming more fast-paced, and communication delays costlier. If a work order changes, it might prove very expensive if those running the shop floor don’t know immediately.
Better Than Refresh
The old, easy, solution to these “real-time” needs for web applications was to instruct users to refresh a page frequently, or to build in a mechanism to force a page to completely refresh itself at some predetermined interval. But that’s clumsy, and inefficient. With AJAX applications we spend lots of time engineering solutions that asynchronously fetch small subsets of data, only as they are needed. Wholesale refreshes, whether user-initiated or automatic, run completely counter to that philosophy and negate any efficiencies gained by that architecture. Wearing out our users’ F5 keys isn’t a very good solution.
SignalR to the Rescue
SignalR was introduced by Microsoft in 2013 to simplify real-time push communication to clients. Leveraging “WebSockets” when available, and other fallback protocols when it’s not, SignalR is a great tool for developers to transform their .NET web applications into real-time, connected applications. Most modern browsers, including Chrome, Firefox, Edge, Internet Explorer, Safari, and Opera, support the WebSocket protocol. Older browsers, such as Internet Explorer 8 and 9 do not, but they can still support SignalR by falling back to a less efficient “long-polling” protocol. For non-browser clients, such as Windows Desktop or Silverlight applications, SignalR can be used as well. Windows 8 or higher, along with .NET 4.5 supports WebSockets, while older versions and Silverlight applications fall back to Long Polling. There is a good overview of the different transports available here.
Getting Started
Getting started with SignalR is remarkably easy. Available as a Nuget package, you can have all of the SignalR dependencies included in your application in a couple of minutes.
Because SignalR facilitates real time communication between a server and clients, it requires some setup on both sides. First, you need to set up the communication pipelines. Initially, that’s as simple as calling the OWIN method “app.MapSignalR()” method in your application’s Startup class:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.MapSignalR();
}
}
Next, we to build a “hub” to publish messages from the server side. Here, we need to make some design decisions. What events will we capture and publish? What should the notifications look like?
Your “hub” class will inherit from SignalR’s abstract “Hub” class. In a manufacturing example, I want to be able to notify all clients when a production run has begun or when its status changes.
public class StatusHub : Hub
{
public void AddProductionRun(ProductionRunViewModel update)
{
Clients.All.AddProductionRun(update);
}
public void UpdateProductionRun(ProductionRunViewModel update)
{
Clients.All.UpdateProductionRun(update);
}
}
This is where SignalR becomes pretty powerful. While I provided a method implementing “AddProductionRun”, the “Clients.All.AddProductionRun” method is dynamic. All that you must do to send a message to all clients is to call that method here. SignalR does the rest of the plumbing for you. Some approaches even bypass creating the hub methods and opt to call the dynamic methods directly from the application.
Now that we have a Hub, we can send messages. In the example, here’s what it might look like for the controller action that starts a production run. Note that this can be fairly painlessly appended to any existing .NET server side action.
[HttpPost]
public ActionResult StartRun(ProductionRunModel model)
{
//Perform business logic
...
var hub = GlobalHost.ConnectionManager.GetHubContext();
hub.AddProductionRun(new ProductionRunViewModel()
{
LineId = model.LineId,
ItemId = model.ItemId,
ItemName = model.ItemName
});
...
}
Making the Client Listen
Now that messages are being published, we need to make our clients listen. In this case it involves including the JavaScript library for SignalR and connecting SignalR to my application. SignalR’s client is a jQuery plugin, so you’ll need to be sure jQuery is included as well.
// Reference the auto-generated proxy for the hub.
var status = $.connection.statusHub;
// Create a function that the hub can call back to display messages.
status.client.AddProductionRun = function (productionRun) {
viewModel.AddProductionRun(productionRun);
};
Different Approaches
In this instance, I opted to use a more verbose message structure. I could notify the client that a process had completed, and include everything in that data transfer object that the client needs to know to update itself. However, in some instances, the data on the client may be more complex. It might be difficult to fully communicate the necessary changes to the client in a single object. In these scenarios you could opt to send the client a very simple notification, alerting it that a change had occurred, and the client can separately retrieve a new set of data. While somewhat less efficient overall (requiring two separate calls), it can still perform quickly, and can greatly simplify the messaging structures.
Am I Connected?
This is the real world, and things break. Connections drop, signals weaken, and servers reboot. One danger of giving users a “real-time” application is that they might not know when something breaks—and what they are seeing is no longer real-time. Thankfully, SignalR provides a “disconnected” event, so that the client can respond when the server has stopped communicating. In my example, I displayed a “plugged in” icon when the client was connected, and a warning “unplugged” message when the connection dropped. Integrating that with my view model required the following:
function startConnection() {
$.connection.hub.start().done(function () {
viewModel.IsConnected(true);
});
}
startConnection();
$.connection.hub.disconnected(function () {
viewModel.IsConnected(false);
setTimeout(startConnection, 5000); // Restart after 5 seconds.
});
When SignalR disconnects, I notify my view model and attempt to reconnect every 5 seconds. When the connection is restored, the view model is notified and can respond accordingly.
Wow Factor
SignalR provides a remarkably easy and powerful solution to bring real-time two-way communication to your applications. You can provide the real-time experience in the types of line-of-business applications that we are growing to expect and give your application a “Wow” factor—for a small amount of effort and without ramping up on a complicated tool chain. Making web applications update “automagically” from changes initiated elsewhere is a powerful tool, and Microsoft has made it extremely accessible to developers via SignalR.
Leave a Reply