Monday, February 13, 2012

SignalR: My journey into .NET Server-to-Client communications - first bumps on the road

SignalR itself says that it's an async signalling library for .NET, but I see it as a very convenient way to call JS functions from server-side and server-side functions from JS.  The official page is here. I have first read about this library in Scott Hanselman's blog and as I'm now starting a new hobby-project, I've decided to try it out.

SignalR has its NuGet package, so I've installed it from there.

Before moving to anything more complex, I wanted to create a simple, "proof-of-concept", where the client just sends a message to the server, when the page is loaded and server replies back with some initial set of data. Again, for this initial phase, I'm trying to keep things simple, so I'm using Hubs. My initial hub is here:
    [HubName("myHub")]
    public class MyHub: Hub
    {
        public void Register()
        { 
            var result = new Order[] {
                new Order
                {
                    OrderId = 1,
                    Product= "lorem"
                },
                new Order
                {
                    OrderId = 2,
                    Product = "ipsum"
                }
            };
            Caller.registerCallback(result);
        }
    }
Now, here it's all pretty simple: we inherit the class from SignalR.Hubs.Hub, to indicate that it's a hub. A hub is the class, which will receive communications from the client side. We're also decorating it with HubName attribute, which specifies the name of our hub, which we'll use in JS and the ending of our url for cummunications.

The class contains a single method - Register (I could have chosen any name I want) which will be called from the client side and return a hard-coded list of orders via a client callback. See the Caller property, that I'm using to invoke the registerCallback? (that's the name of the function, which we'll define in JS, which will handle our results) It is used to communicate only with the client, which just invoked our method - and it's all automatic :)

Next comes our client side part:

var hub;
$(document).ready(function () {
     hub = $.connection.myHub;

     hub.addOrder = function (addOrderMessage) {
        $(".orderList").append("<li id='" + addOrderMessage.OrderId + "'>" + addOrderMessage.Product+ "</li>");
    };

    hub.registerCallback = function (initialListOfOrders) {
        for (var i = 0; i < initialListOfOrders.length; i++) {
            var order = initialListOfOrders[i];
            hub.addOrder(order);
        }
    };

    $.connection.hub.start(function () { hub.register(); });
});
Again, code is simple enogh - after the page is loaded, we're assigning our hub to a local variable. Then, we're defining two functions on our hub (which both can be invoked from the server side) - addOrder, which takes a single order and adds it to the list and registerCallback, which gets initial list of order.
And on the last line, we're starting the communication process. And that's where I initially hit my first bump.
You see, the example in Scott's blog post starts the hub without passing any function to the start method. Some other exmaples did the same. So initially the ending of my script looked like this:

    ...
    $.connection.hub.start();
    hub.register();

But instead of registering with the server, script threw an exception on hub.register, which stated that:
SignalR: Connection must be started before data can be sent. Call .start() before .send()
Which was rather confusing, given that I was calling my register() method just after calling hub.start(). To make things worse, all the other examples I had, were working perfectly, so I rejected the idea that something is missing in my hosting environment. And it seemed, that nobody else was seeing this issue.
And then I realized - Scott's example isn't calling anything on the hub immediately after it is started - his app only calls hub method on events from the user - and that gives the hub plenty of time to initialize. So after some more googling, I've found, that hub.start() has a callback, which is invoked after the hub is initialized.

Now, you're probably wondering, how does my JS code know about this $.connection.myHub, which I've declared on the server-side? In order for this to work, I need to include two JS files:

<script src="@Url.Content("~/Scripts/jquery.signalR.js")" type="text/javascript"></script>
<script src="@Url.Content("~/signalr/hubs")" type="text/javascript"></script>

The first one is the signalR JQuery plugin. And the second one.... if you'll check your source folder, you won't find anything like it, but it's generated at the runtime by SignalR and includes all the hubs you've delcared.

So that's it for my first post (hopefully - out of many) about SignalR. So far, I'm really happy with this library, as it provides a very simple way for .NET developers to get a very smooth user experience.

Who am I and what will this blog be about

About
To those of you, who do not know me personally - my name is Tomas, and I'm a .NET software developer, currently working and living in the Republic of Ireland. I'm usually mostly interested in the server-side of things and in services (ASMX WS, WCF etc) in particular, because I think that I lack this essential attention to details, needed to write good user-facing code. On the other hand, I've work for several years as a front-end developer - and everyone seeemed happy enough.

What is this blog about?
This blog will be for my software development related talks, usually perhaps related to some explorations into new (to myself at least) technologies, or about some technical problems, which I've successfully solved and whose solution might help some lost soul on the internet.

Why English?
Now this might not be a natural question to those of you, who don't know me. You see, I am a Lithuanian, and so my mother tongue might look like an obvious choice for the language of this blog, but for several reasons, I've decided to write this blog in english. Why?

  • First and foremost - this way I can possibly reach the largest number of readers, as most of software developers know English quite well. I myself search for technical articles only in English. So, if any of the posts here can be of any help to someone - this hugely increases my chances of reaching that  someone .
  • Another reason, although not as important as the first one, is to continually improve my English, so please, those of you, that are native English speakers and see any big mistakes in my writings - please be so kind to let me know about them in the comments.
What will not be published here
I will not be talking here about anything related to my day-to-day work. Even though technologies might sometimes be similar to those I use during my work hours (hey, I'm .NET dev during the daytime too!), but I am only blogging about things, that I do in my free-time.