Blog Archives

SignalR and the Enterprise

SignalR seems to be gaining a lot of momentum at the moment. For those unacquainted with it, it describes itself as an “Async signaling library for .NET to help build real-time, multi-user interactive web applications.”  Having taken a brief look at it my initial impressions were that it was most suited to single page web applications like Twitter. However, since reading Kent Weare’s excellent blog posts on using SignalR with BizTalk, I realised there was much more to it. In fact I believe it can fill a gap in the enterprise development space for a simple, lightweight messaging hub.

Kent’s post shows how the SignalR .NET Client library can be used by any .NET project to interact with a SignalR Hub. This was something I had failed to pick up on originally and really extends the hub paradigm to any server side code including BizTalk (as shown in the blog post) but also WCF Services and Azure too. Kent’s example could equally be implemented using MVC deployed as an Azure Web Role in conjunction with a queue and a Worker Role providing the back end. In fact I spent a little time putting together a simple project based upon this as a learning exercise.

Here are some gotchas I encountered with SignalR and how I overcame them.

Where does the Hub URL come from?

In Kent’s example from Part 2 of the blog, the Notifier uses a URL in the connection to the Hub. At first I could not see where this “magic” URL came from. Referring to the SignalR documentation it becomes clear that the Hub is hosted in a web application and the URL refers to the root of that site.

This also explains where the mysterious ../signalr/hubs script originates from.

<script src="../signalr/hubs"></script> 

When I first added this reference into my client page I couldn’t figure out what it pointed at. My project did not include this script so what is it. In fact it is produced by the Hub and browsing to it when the application is running will render the script for you to observe.

Lowercase naming of Hub methods

As part of my solution I implemented a SignalR Hub with a public function named Subscribe. This is used by client side JavaScript to create a subscription to events of interest.

Hub.cs

    /// <summary>
    /// Quote Hub that is used to asynchronously notify a client when a quote response is available
    /// </summary>
    [SignalR.Hubs.HubName("quoteHub")]
    public class QuoteHub : Hub
    {
        public void Subscribe(String quoteId)
        {
            Groups.Add(Context.ConnectionId, quoteId);
        }
    }

Client.js

hub.Subscribe("@Model.QuoteId.ToString()");

However I kept getting the following script error:

Uncaught TypeError: Object #<Object> has no method ‘Subscribe’

After much head scratching I found that the client script needed to use the function name ‘subscribe’ rather than ‘Subscribe’ i.e. no uppercase ‘S’.

hub.subscribe(&quot;@Model.QuoteId.ToString()&quot;);

Subscription Keys are Strings

My Hub also has a PublishQuoteResults function which is used to publish results of a transaction to clients subscribing to them.

        public void PublishQuoteResults(QuoteResponse quoteResponse)
        {
            // Call the client subscribed to this quote id
            Clients[quoteResponse.QuoteId].quoteComplete(quoteResponse);
        }

I received the following exception…

RuntimeBinderException: The best overloaded method match for ‘SignalR.Hubs.ClientAgent.this[string]’ has some invalid arguments

This turned out to be because the key I was supplying for the Clients object (quoteResponse.QuoteId) was not a string (in fact it was a Guid). Appending ToString() to it resolved this issue.

 

Use of Uninitialised Connection

The first cut of my client script was as follows:

&lt;script type=&quot;text/javascript&quot;&gt;
    $(function () {
        var hub = $.connection.quoteHub;

        hub.quoteComplete = function(response) {
            $(&quot;#divResponseDetails&quot;).html(&quot;&lt;p&gt;&quot; + response.QuoteId + &quot; has a status of &quot; + response.Status + &quot;&lt;/p&gt;&quot;);
            $(&quot;#divResponseDetails&quot;).show();
        };

        $.connection.hub.start();
        hub.subscribe(&quot;@Model.QuoteId.ToString()&quot;);

    });
&lt;/script&gt;

This version failed on the last line (the call to subscribe) with the following error:

Uncaught SignalR: Connection has not been fully initialized. Use .start().done() or .start().fail() to run logic after the connection has started.

This exception message is very helpful and identifies that a continuation should be used to execute the last line after the hub has fully started. The last two lines were adapted as follows and all was well.

 $.connection.hub.start()
            .done(function() { hub.subscribe(&quot;@Model.QuoteId.ToString()&quot;) });

In my view SignalR is a great new addition to the toolbox of the Enterprise developer in a variety of architectures and configurations. I hope my observations help overcome some of the little gotchas that can crop up.

Advertisements