In modern applications the end users want to get their data. They want it now, they want it up-to date. In fact it does not matter whether these are pure web application, native desktop installations or mobile apps: everybody wants his data now!

For .NET-minded developers there are a numbers of options to implement near-real-time push style communication from the server/the services to the clients/consumers. You can choose plain HTTP or the super-new WebSockets features available in .NET 4.5 together with Windows 8 and Windows Server 2012. But the coolest and increasingly popular approach is to use a new framework: ASP.NET SignalR.

While it is not intended- and surely beyond the scope of this ebook - to give an introduction or even deep dive into SignalR, we need to have a look at some concepts and code in order to realize a smooth integration of SignalR and AngularJS.

The final goal of this chapter is to have an AngularJS-style integration of calling and listening to server-side SignalR push services.

A Simple SignalR Hub

Our server-side hub for demonstration in this chapter will be a server time emitting push service.

It has one method which can be called as the inbound API. And we will add a background worker in ASP.NET which will periodically push the server time every 5 seconds to all connected clients. For the sake of clarification that hubs do not really need to have both, an inbound and outbound API we will use two different hubs:

  • one for calling the server time from the client (kind of a Web API replacement, but based on the hubs protocol) - aka inbound API.
  • another one for using the hub context to call into connected clients - aka outbound API.

using Microsoft.AspNet.SignalR;
using System;

namespace Henriquatre.Integration.SignalR
{
    public class ServerTimeHub : Hub
    {
        public string GetServerTime()
        {
            return DateTime.UtcNow.ToString();
        }
    }

    public class ClientPushHub : Hub
    {
    }
}

How to Schedule Frequent Pushes on the Server

The official and recommended approach to run scheduled background work in ASP.NET is to implement an IRegisteredObject with the ASP.NET runtime and hook it up at application start.

In the end it can be a simple as this:

using Microsoft.AspNet.SignalR;
using System;
using System.Threading;
using System.Web.Hosting;

namespace Henriquatre.Integration.SignalR
{
    public class BackgroundServerTimeTimer : IRegisteredObject
    {
        private Timer taskTimer;
        private IHubContext hub;

        public BackgroundServerTimeTimer()
        {
            HostingEnvironment.RegisterObject(this);

            hub = GlobalHost.ConnectionManager.GetHubContext<ClientPushHub>();          

            taskTimer = new Timer(OnTimerElapsed, null,
                TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5));
        }

        private void OnTimerElapsed(object sender)
        {
            hub.Clients.All.serverTime(DateTime.UtcNow.ToString());
        }

        public void Stop(bool immediate)
        {
            taskTimer.Dispose();

            HostingEnvironment.UnregisterObject(this);
        }
    }
}

Inside of the constructor we get a reference to the hub context for our ClientsPushHub. This reference is then used in the timer's event to call all registered clients and actually call the serverTime 'method' on the client side.

In global.asax.cs we can simply create and set up the registered objects (we have another in place here for pushing performance data, see below in this chapter) and get our hubs pipeline working by adding the hub routes to the routes table. In this case we enable CORS as the SignalR services are located on a different server than the client-side HTML and JavaScript application.

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.AspNet.SignalR;

namespace Henriquatre.Integration.SignalR
{
    public class MvcApplication : System.Web.HttpApplication
    {
        private BackgroundServerTimeTimer bstt;
        private BackgroundPerformanceDataTimer bpdt;

        protected void Application_Start()
        {
            bstt = new BackgroundServerTimeTimer();
            bpdt = new BackgroundPerformanceDataTimer();

            RouteTable.Routes.MapHubs(new HubConfiguration
                { EnableCrossDomain = true });

            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
    }
}

Speaking of the JavaScript clients, let's have a quick look at how to build a JS-based application with the Microsoft-provided JavaScript APIs.

SignalR JavaScript Client APIs

The current version (1.1 beta as of writing) provides two jQuery-based JavaScript APIs:

  • with JS proxy: the client uses a dynamically generated JS file as a proxy description for the hubs ('SignalR-WSDL', if you like).
  • without JS proxy: there is no static metadata involved at all and we wire up events and do method calls based on event and method names.

For a smooth integration between SignalR's JavaScript and AngularJS we are going to choose the no proxy route (no pun intended!).

An AngularJS Service for Integrating Hubs

With the above in mind we can write an AngularJS service that wraps and encapsulates the SignalR communication.

The following code shows the fully functional service. We are actually creating a kind of factory which the user of the service can then do different things with by working on the internally created runtime proxy object:

  • register local JavaScript methods which can be called by a server-side SignalR hub (local events, if you will) - this happens through the on method.
  • unregister a local JavaScript method from the runtime proxy by using off.
  • calling server-side hub methods by using the invoke function.

For convenience, we are also exposing the SignalR hub connection in order to get relevant data like e.g. the connection ID.

'use strict';

app.factory('signalRHubProxy', ['$rootScope', 'signalRServer',
    function ($rootScope, signalRServer) {
        function signalRHubProxyFactory(serverUrl, hubName, startOptions) {
            var connection = $.hubConnection(signalRServer);
            var proxy = connection.createHubProxy(hubName);
            connection.start(startOptions).done(function () { });           

            return {
                on: function (eventName, callback) {
                    proxy.on(eventName, function (result) {
                        $rootScope.$apply(function () {
                            if (callback) {
                                callback(result);
                            }
                        });
                    });
                },
                off: function (eventName, callback) {
                    proxy.off(eventName, function (result) {
                        $rootScope.$apply(function () {
                            if (callback) {
                                callback(result);
                            }
                        });
                    });
                },
                invoke: function (methodName, callback) {
                    proxy.invoke(methodName)
                        .done(function (result) {
                            $rootScope.$apply(function () {
                                if (callback) {
                                    callback(result);
                                }
                            });
                        });
                },
                connection: connection
            };
        };

        return signalRHubProxyFactory;   
}]);

The sample controller below illustrates how to use the SignalR AngularJS service. First, we get a reference to that 'factory' object by passing in the base URL of the SignalR hubs. This is essential when using the hubs in a cross-domain scenario. Optionally we can also pass the documented SignalR client configuration options.

From there on it is straight-forward to hook up client events and invoke/call methods on the server.

function ServerTimeController($scope, signalRHubProxy) {
    var clientPushHubProxy = signalRHubProxy(
        signalRHubProxy.defaultServer, 'clientPushHub',
            { logging: true });
    var serverTimeHubProxy = signalRHubProxy(
        signalRHubProxy.defaultServer, 'serverTimeHub');

    clientPushHubProxy.on('serverTime', function (data) {
        $scope.currentServerTime = data;
        var x = clientPushHubProxy.connection.id;
    });  

    $scope.getServerTime = function () {
        serverTimeHubProxy.invoke('getServerTime', function (data) {
            $scope.currentServerTimeManually = data;
        });
    };
};

How Does it Works?

AngularJS internally works with cycles when it comes to execute data binding and to determine when values of the model have changed. One of the cycles is the digesting cycle where Angular checks for model changes by processing and evaluating all registered watch expressions. But if model changes happen outside of the AngularJS world - in our case we get a callback executed externally, not happening in any controller or AngularJS artifact - then we need to instruct Angular that the model actually has changed.

This is what $apply is for: available on the scope, it triggers (or forces) the digesting cycle by calling$digest internally (API Reference: ng.$rootScope.Scope) and this is what we need to use in our SignalR-wrapping service.

on: function (eventName, callback) {
    proxy.on(eventName, function (result) {
       $rootScope.$apply(function () {
          if (callback) {
             callback(result);
          }
       });
    });
}

Conclusion

Last but not least we are showing you a running embedded example of the AngularJS-SignalR integration. This does not only use the already shown and discussed two hubs for the server time but a new one which frequently pushes current performance counter data (CPU values, in this case) to the connected clients.