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
Event is almost sold out!
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
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.
Justin Schwartzenberger
Have seen some questions about concurrency and went in search of an answer:
Justin Schwartzenberger
From what I've seen so far on SignalR, I think a good way to sum up how to approach it is:
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.
Marius Schulz
Great post! I really liked that you showed how easy it is to use SignalR with Knockout.js ...
David Fox
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 ;)
George R
There's something jarring about using this with MVC. It blurs the line between the controller's responsibility.
Brandon E
Very nice example. Stumbled upon your blog while researching how to implement Knockout with MVC.
Brian N
Loved your post. Showed me how to integrate two powerful entites to create responsive application. Trying to use this to develop a chat application.
Justin Schwartzenberger
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
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
@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?
Amazing article. Love your style of describing things. So simple and easy to understand.
Gareth Bradley
Definitely got the gears ticking! Im thinking of a Agile Storyboard software for Geo-located teams......
Thanks for sharing. Signalr is something I have on my 'new things to learn' list for this year.
Tim Cools
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..
Or is there something I'm overlooking?
boxer
Hi,
thanks for this very interesting article. I have one question regarding SignalR JavaScript code:
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?