Learn how to use SignalR and Knockout in an ASP.NET MVC 3 web application to handle real-time UX updates.

Posted on December 7th, 2011

SignalR, built by David Fowler () and Damian Edwards (), is an async signaling library for ASP.NET to help build real-time, multi-user interactive web applications. Adding it to an ASP.NET MVC 3 web application is ridiculously easy. Mashing it up with a client side library like Knockout can be downright magical! Wondering how to get started using SignalR with MVC? How about we new up some sample code and dive in to see what all this "real-time" fuss is about?

I posted about using Knockout to handle real-time UX updates in an ASP.NET MVC 3 application back in July of 2011. It covered creating a product page in which the stock counts for a product would update over time without refreshing the page. The Knockout code handled the display updates to the DOM like a champ, but the "real-time" update calls were handled with some pseudo-polling using the JavaScript setTimeout method. We can build a similar sample to that one, but this time we can implement SignalR for the count updates.

Our sample application this time around will be an Event Ticket page that allows a visitor to buy a ticket with the click of a button. The page will display the current available ticket count, show a message when the stock level of the tickets is getting low, and will disable the ability to buy a ticket when the tickets are out of stock.

Packages to Install

First we need to install the SignalR NuGet package into an ASP.NET MVC 3 project.

Install-Package SignalR

Out of the box, SignalR does not support versions of Internet Explorer prior to 8. There is an additional package, JSON2, that can be added to provide this support. We will add this to our sample project.

Install-Package Json2

Finally, we need to add the Knockout package:

Install-Package knockoutjs

Scripts to Reference

In our _Layout.cshtml file we need to reference jQuery, the Json2 script, the SignalR script and the Knockout script.





Note the order here. The Json2, SignalR and Knockout libraries require the jQuery library. In addition, the SignalR library requires Json2 to be referenced before it to include the pre IE 8 support.

Implementing a SignalR Hub

The Hub logic in SignalR allows us to broadcast and interact with multiple connected clients (for more information see the official documentation). Our event ticket sample needs to allow clients to click and buy a ticket. It also needs to keep all clients informed of the current available ticket count. We will create a ticket hub that will inherit from the SignalR.Hubs.Hub class. The hub will contain a method named GetTicketCount for requesting the current ticket count and a method named BuyTicket to buy a ticket. For the sake of this example, we will create a static field to store the total ticket count.

using SignalR.Hubs;

namespace Mvc3SignalR.Models
{
    public class TicketHub : Hub
    {
        static int TotalTickets = 10;

        public void GetTicketCount()
        {
            Clients.updateTicketCount(TotalTickets);
        }

        public void BuyTicket()
        {
            if(TotalTickets > 0)
                TotalTickets -= 1;
            Clients.updateTicketCount(TotalTickets);
        }
    }
}

The SignalR.Hubs.Hub class has a public property named Clients for accessing the connected clients. Both of our methods will call a dynamic method that we have created off of the Clients property called updateTicketCount. This method takes in the current ticket count. When the hub executes this method it is essentially telling the clients that this method was called with the TotalTickets value.

The View from the Client

We will run our event tickets sample page from a HomeController with an Index action method and a corresponding Index.cshtml view file. The markup for the display in the Index.cshtml file:

@{ ViewBag.Title = "Ticket Sales"; }

Event Tickets

To learn the details behind using Knockout, read through the July post.

The JavaScript consisting of Knockout code and SignalR code in the same Index.cshtml file:



The first script tag references the route /signalr/hubs that is created by the SignalR framework to expose accessibility to the hubs. Note that the usage of the hubs does not require any additions to the routing table (for example, in the Global.asax.cs file).

The ticketDataViewModel is our Knockout view model. It has an observable for the Available ticket count and a function for handling the BuyTicket event. We also add 2 dependent observable methods to handle a check on the ticket count getting low and the ability to buy a ticket. With those declared we can apply the Knockout view model to the TicketSummary element block.

Next up is the SignalR JavaScript code. We declare a variable named tickets outside of the jQuery document ready block so that it is available to the ticketDataViewModel.BuyTicket method. Then within the document ready block we instantiate a connection to the TicketHub. A method is created for handling the calls from the server to the updateTicketCount method. Our client JavaScript function receives the data (in this case the data parameter contains the total ticket count available) and sets the Knockout ticketDataViewModel.Available observable. When the server hub broadcasts a call to the updateTicketCount method, all clients connected to the hub will receive the broadcast and update the observable, which triggers Knockout to update the display.

The final bit of JavaScript on the client is the call to start the hub connection. This method can take in a function to execute when the connection is established. We need to update the display with the current ticket availability count as soon as a new client connects. This function calls the getTicketCount method in the TicketHub. Since that method on the server broadcasts out a call to the updateTicketCount method on each connected client, the tickets.updateTicketCount function we declared on the client will get called (which leads to our Knockout observable update and thus our display update).

Buy Buy Buy!

With everything wired up, we can F5 the project and start buying some tickets. Since we are trying to illustrate and learn about the power of SignalR it is imperative, dare I say of the utmost importance, that we fire up at least one other instance of a browser and navigate to the same URL to fully experience the magic.

Note that this isn't a Session type architecture, so feel free to ignore that ingrained reaction to launch a different user agent (Chrome, FireFox, etc). Go nuts and copy the url into another tab in the same browser instance that was launched when you ran the debugging.

Start buying tickets in each browser tab/instance and watch the other update in real-time! Spam the "buy a ticket" button and watch the availability count drop to oh noes levels. Purchase the last remaining tickets with the browser on your right and watch all connected browser clients have their "Buy a Ticket" button become disabled at the same time.

Now get out there and start implementing all of those potential ideas cultivating in your mind, because this stuff is a blast to work with!

Download the code

Some Additional SignalR Resources

Post by Ben Dornis () Introducing SignalR.EventStream.

Maarten Balliauw's recent post on Using SignalR to broadcast a slide deck.

Scott Hanselman's post on Asynchronous scalable web applications with real-time persistent long-running connections with SignalR.

Anoop Madhusudanan's () post on KsigDo Task Pad – Real-Time UI View Model syncing across users with ASP.NET, SignalR, Knockout MVVM and EF .

And, of course, a mention of JabbR is manditory! :)

Discussion

Ian Patrick Hughes
08 Dec, 2011 05:35 PM

That is a really a nice little project. I just started playing around with SignalIR and BackBone.js recently and was really impressed with what you can do with it.

08 Dec, 2011 07:36 PM

Have seen some questions about concurrency and went in search of an answer:

How does concurrency need to be approached?
From Damian Edwards: There is not shared hub instance. it's like an MVC controller or Web Forms page, new instance per request/connection. If you have statics on your hub you're using for state, you have to manage the concurrency yourself.

08 Dec, 2011 07:47 PM

From what I've seen so far on SignalR, I think a good way to sum up how to approach it is:

SignalR is an async signaling library for ASP.NET that focuses on it's single responsibility.

That may help to provide some clarity when we start thinking about what SignalR will do for us in different scenarios and what we need to think about handling within other app logic.

10 Dec, 2011 07:59 PM

Great post! I really liked that you showed how easy it is to use SignalR with Knockout.js ...

David Fox
David Fox
12 Dec, 2011 10:06 PM

Sweet sample project--thanks! One caveat (more of a derp moment) I forgot about the CSS classes "Hidden" and "ImportantMessage" and couldn't figure out why the sold out message initially displayed ;)

.Hidden { display:none; } /* derp */
George R
George R
14 Dec, 2011 04:19 AM

There's something jarring about using this with MVC. It blurs the line between the controller's responsibility.

Brandon E
Brandon E
06 Jan, 2012 05:00 PM

Very nice example. Stumbled upon your blog while researching how to implement Knockout with MVC.

Brian N
Brian N
09 Jan, 2012 04:38 AM

Loved your post. Showed me how to integrate two powerful entites to create responsive application. Trying to use this to develop a chat application.

10 Jan, 2012 04:40 AM

I was hoping that this post would be something that could "get the gears turning" so to speak and show people a way to get started using SignalR and some Knockout. These two pieces of tech, when combined together, open up some new possibilities in web applications and client side UX design.

BTW - I really appreciate everyone's feedback, contributions and insight. I'm glad this site can be a place that helps fellow developers learn and discuss code in a supportive manner.

Sam
Sam
08 Mar, 2012 02:32 PM

So, if one wanted to persist the data captured during a BuyTicket call to a database (to keep track of which users bought tickets, etc.) you would do that db communication in the BuyTicket() method of the TicketHub model?

I'm new to MVC and am trying to reconcile it to the webforms mindset that I am in. I thought most db communication (reads/writes) are a part of the controller's responsibility, but I don't see where that would fit into this example.

Joel
Joel
09 Apr, 2012 03:42 PM

@Sam - Yes, that is where you would place any saved information. You'll also want to have some mech for capturing User ID (something like ASP.NET Forms authentication, etc)

I have one question about SignalR, what if your MVC project is hosted on something like Azure, where multiple instances of your project is running? Will SignalR still be able to communicate between all the Azure instances?

melina
11 Apr, 2012 08:00 PM

Amazing article. Love your style of describing things. So simple and easy to understand.

15 Apr, 2012 10:42 AM

Definitely got the gears ticking! Im thinking of a Agile Storyboard software for Geo-located teams......

jaoord
27 Apr, 2012 07:00 AM

Thanks for sharing. Signalr is something I have on my 'new things to learn' list for this year.

27 Apr, 2012 08:40 AM

Nice example! Thanks for the effort...

Isn't it sufficient to call the Caller in the GetTicketCount method, instead of all the Clients? This would prevent that each client is called when a new client connects..

public void GetTicketCount()
{
  Caller.updateTicketCount(TotalTickets);
}

Or is there something I'm overlooking?

boxer
boxer
13 May, 2012 07:50 PM

Hi,

thanks for this very interesting article. I have one question regarding SignalR JavaScript code:

$(function () {
    tickets = $.connection.ticketHub;
    tickets.updateTicketCount = function (data) { ticketDataViewModel.Available(data); };
    $.connection.hub.start(function () { tickets.getTicketCount(); });
});

Why when we declare tickets variable we use name "ticketHub"? I assume this is the name of the controller and in this example it's "TicketHub" (capitalized). Why is that?

Your Comments

Preview