ASP.NET MVC 3 Real Time Collaborative Apps with SignalR

Abstract: In this article, we’ll create a simple blog app using ASP.NET MVC 3 and SignalR, that will allow multiple collaborators to review the same article in real-time

In this article, we’ll create a simple blog app using ASP.NET MVC 3 and SignalR, that will allow multiple collaborators to review the same article in real-time.

What is SignalR?

By the book – It is an asynchronous signaling framework that helps maintain persistent connections between client and server.

Not by the book – A real cool framework built on top of ASP.NET (server side) and a JavaScript library (on the client side) that helps build collaborative web apps in a jiffy. David Fowler and Damien Edwards maintain the SignalR project on GitHub

SignalR abstracts the raw technique of keeping connections open between a web client and a web server. Behind the scenes it uses an existing technique called long polling. However as the WebSockets protocol crystallizes, it could very well use WebSockets transparently, behind the scenes.

SignalR uses the task parallel library on the server side. It scales well as of now. However, work is in progress to make it better.

Why do I need SignalR?

Most common examples where long polling is used are stock tickers or chat clients. However as we will see today SignalR throws open a whole new set of possibilities, enabling rich collaborative applications on ASP.NET.

Solving a business problem with SignalR

Enough theory, let’s get down to the real world. I am sure most of us have at some point used Google docs and recently if you have noticed, you can share it with multiple people and now you can even have two people edit it at the same time. Each person gets a different colored caret to track who is doing what.

Wouldn’t it be cool if you could embed such ’Review’ functionality to your app? Imagine filling out a form and then sending it out for review only to find out someone down the chain rejected it after a week because of a typo? Now imagine what if you could do instant reviews and everyone could give their inputs and sign off on the document at the

same time?

In this article we’ll create a simple blog app that will allow multiple collaborators to review an article at real-time.

Step 4: Scaffold the Controllers and Views

Step 5: Add the ‘Review’ Controller and View

By default, the scaffolding generates Index, Create Edit and Delete views each mapping to their respective actions. We will leave these as is and add a new Action called ‘Review’. To do this we’ll simply copy the Edit Action and rename it.

In the BlogPostController, add the Review action methods as follows:

Under Views\BlogPost add the Review.cshtml

In the Index.cshtml, add a new link to the ‘Review’ view.

Step 6: Getting SignalR into action

On the Server Side

In your web project create a folder called SignalR (You can call it anything you want, in a real life project this as server side component and could very well reside in a dll of it’s own). In the SignalR folder add a class BlogHub.cs. Update the class to look as follows:

The class is decorated with the ‘HubName’ attribute that takes a string parameter. The string parameter is the ‘client side’ name of the ‘Hub’. The Hub as the name suggests keeps track of the ‘subscribers’ and ‘publishes’ or broadcasts the messages that are passed to it by the subscribers.

In the above snippet, the Send method is called from the web page via JavaScript. SignalR framework takes care of the appropriate hookups.

Each ‘client’ that is web page, interested in receiving events from the hub subscribes to it and then implements the addMessage function. So when the Hub fires back, all clients handle the event.

The ‘/signalr/hubs’ reference is showing the green squiggly for a reason. It doesn’t exist as design time. It will be generated at run-time.

Bringing Diff-Match-Patch into the party

We will use Neil Fraiser’s excellent implementation from http://code.google.com/p/google-diff-match-patch/ There are multiple implementations available and you could use it on the server side (C# library) or client side (javascript library). I decided to let the client do all the hard work, so I used the javascript library. Add the script reference to Review.cshtml

Add a placeholder to Review.cshtml that will hold the session id. This session Id is used to identify which client initiated the request.

Hooking up the hub and the publish handlers

Add the following script block to the Review.cshtml

The code comments are pretty self-explanatory and the sequence of events is as follows:

Two clients come to review the same BlogPost

One makes changes to the BlogPost’s Article (aka BlogBody) that is ‘Send’ to the hub with an identifier as to who initiated the change (Session Id).

The hub simply lobs it back to all the clients by calling the ‘addMessage’ method.

Now both the clients receive the addMessage ‘signal’. Only the one that has a session Id that doesn’t match goes ahead and does a diff-match-patch.

Other Miscellaneous changes

Update _Layout.cshtml ‘s jquery script reference to the version you have.

Make things pretty by changing the default title of the app from ‘My MVC Application’ in the _Layout.cshtml to something nicer.

Change the Index.cshtml and update the header from ‘BlogBody’ to ‘Article’

Change the Global.asax ‘s RegisterRoutes method to navigate to the BlogPost’s Index directly

Step 7: Let it Roll

Run the application and create a new Post. Save it. Your Index page should look something like the following

Click on Review to go the review page. Press Ctrl+N to fire up a new browser instance with same page. Hit F5 to update session Id in the new Browser instance.

Line up the browsers side by side. Type in one and watch the other one change.

Conclusion

SignalR is a powerful and easy to use framework that enables permanent connections and Asynchronous signaling in ASP.NET. Its potential uses include but not limited to

Collaborative editing tools like the one we saw here.

Highly dynamic apps where certain changes can be pushed in near real time for example, a Cart can signal a purchase completion enabling dynamic updates of ‘stock numbers’ for end users

News tickers with real-time feeds and many many more.

Long-polling is not a new thing, however now we have a fantastic abstraction to use in ASP.NET.

So what will You do with SignalR?

Full Disclosure: I have reproduced parts of this article from an article on the same topic in my personal blog.

Sumit is a .NET consultant and has been working on Microsoft Technologies since his college days. He edits, he codes and he manages content when at work. C# is his first love, but he is often seen flirting with Java and Objective C. You can follow him on twitter at @sumitkm or email him at sumitkm [at] gmail

User Feedback

Comment posted by
Jags
on Tuesday, January 3, 2012 12:08 AM

A very nicely explained article! I liked the demo app you choose for explaining the power of SignalR. Before reading this article I was hoping the example not to be based on a chat app :)
SignalR is certainly going to be a nice addition to our web development library and in coming days, a must have.

Comment posted by
Sumit
on Wednesday, January 4, 2012 12:51 AM

Thanks Jags, glad you liked the use case. I try to keep use cases as close to real life as possible :).

Do share your SignalR adventures with us if you get a chance!

Comment posted by
Jeff
on Wednesday, January 4, 2012 3:20 PM

Very good. I noticed one bug that I had to change to get this to work. In the call to blog.send (Review.cshtml) I had to remove the last, boolean parameter. The Send method didn't have this.

Comment posted by
Sumit
on Wednesday, January 4, 2012 5:24 PM

@Jeff: Thanks for pointing out the discrepancy. My bad! We'll update the screenshot.

Comment posted by
Sumit
on Wednesday, January 4, 2012 10:49 PM

UPDATE: Thanks to @Jeff we have updated the screenshot to remove the last boolean parameter.

Comment posted by
Jon Davis
on Thursday, January 5, 2012 4:19 PM

Followed this article to detail. Ending up with 500 error on keyup, "'Send' could not be resolved." @ SignalR.Hubs.HubDispatcher.OnReceivedAsync(String clientId, String data) +693

Comment posted by
Jon Davis
on Thursday, January 5, 2012 4:19 PM

.. hrm, I had this web page up for 24 hours, I see new comments, the problem may be addressed. Thanks.

Comment posted by
Sumit
on Saturday, January 7, 2012 3:05 AM

Hello Jon,
Apologies for the delay in response. Can you send your source-code to my email address. I'll take a look. Sounds like an issue hooking up the Hub to the client.
Thanks and Regards,
Sumit.

Comment posted by
jose pardo
on Friday, January 13, 2012 4:08 AM

Hello
Thank you for this clear article. Since i've been evaluating this library , I have a question. Does SignalR works in a web farm environment?

Comment posted by
victorjhon
on Friday, January 13, 2012 4:55 AM

Nice post, Author has a very sharp mind.

<a href="http://www.testkiller.me/"><b>TestKiller<

Comment posted by
victorjhon
on Friday, January 13, 2012 5:01 AM

Nice post, Author has a very sharp mind.

<a href="http://www.testkiller.me/"><b>TestKiller<

Comment posted by
Nyron
on Friday, January 13, 2012 5:25 PM

Excellent article, very well explain. Is there any security concerns with placing the users session ID in a hidden field?

Comment posted by
Sumit
on Saturday, January 14, 2012 2:45 PM

@Jose Pardo : Jose I am not sure about the status of the SignalR.ScaleOut branch. But that was the namespace being developed by the authors of SIgnalR for the web farm configuration.

@Nyron: Instead of a hidden field you can use the ViewBag or use the SignalR provided clientId propoerty as defined here: http://stackoverflow.com/questions/7872589/call-specific-client-from-signalr

Comment posted by
johnny
on Monday, January 16, 2012 8:58 AM

Definitely pretty cool! But I have problems with SignalR messing up my conventions, my Stylecop doesn't allow lowercase methods in C#, while uppercase doesn't fit js conventions. Also, I don't like the fact we're dealing with dynamic objects on members like 'Clients' where there's absolutely no need for it. I'd rather have my own interfaces so my server is still typesafe.

Comment posted by
Rob Lynch
on Tuesday, January 17, 2012 11:32 AM

Very intesting.. I did notice when playing with this that the connection seemed to be open for all of the windows i could open. However. When i added a "new" post, and edited it, all the open windows that were pointing @ post 1 were getting post 2's new text. How would you suggest making this specific to a particular post and not do a broardcast to all the open browsers no matter which post they had opened.

Comment posted by
shakti singh
on Wednesday, January 18, 2012 1:12 AM

very nice..

Comment posted by
shakti singh
on Wednesday, January 18, 2012 1:15 AM

very nice..

Comment posted by
Sumit
on Thursday, January 19, 2012 3:43 AM

@Rob Lynch,

Hi Rob, you can simply pass the Blog Id along with the session id in the Send method, then lob it back in the addMEssage method and compare it in Javascript like I am doing for session id.

It's a nice little bug you found there. I'll try and update the code and screenshots for the same.

Thanks and Regards,
Sumit.

Comment posted by
Zaia Yokhanna
on Thursday, February 2, 2012 1:06 PM

I noticed that you are using intranet template,is it applicable to internet?

Comment posted by
Zaia Yokhanna
on Thursday, February 2, 2012 2:22 PM

I noticed that you are using intranet template,is it applicable to internet?

Comment posted by
Zaia Yokhanna
on Friday, February 3, 2012 12:59 PM

I tried it in my project MVC4 using internet with plan textarea, it works fine. However, if you collaborating via rtf textarea such as simple tinymce it stops working.

Comment posted by
Sumit
on Sunday, February 5, 2012 5:41 AM

@Zaia,
Any RTF editor has markup information embedded in them. So you may have to use the appropriate property to retrieve the entire text including the markup and then do the diff. Same with the merge.
If you can send me your code sample I will try to take a look.
Thanks and Regards,
Sumit.

Comment posted by
Zaia Yokhanna
on Thursday, March 8, 2012 7:10 PM

Sumit, how would you prefer to receive the code sample?

Comment posted by
Sumit
on Sunday, March 11, 2012 10:51 PM

@Zaia, my email address is sumitkm [at] gmail [dot] com. You can send non-binaries there, or you could use a skydrive/dropbox public location and mail me the URL.

Comment posted by
Salinas
on Monday, June 11, 2012 8:11 PM

Hi Zaia, I'm building an MVC4 application, does your example work with MVC 4?

Comment posted by
vali
on Thursday, July 5, 2012 4:55 AM

Very good presentation. Built it and ran it just from article without using the attached source code. Well done!

Comment posted by
mike wiegand
on Friday, July 20, 2012 8:11 AM

Downloaded the source, F5, and it fires up, but ya, 2 side by side browsers don't reflect changes. Nothing is referencing the BlogHub class, so how would this ever work?

Comment posted by
Sumit
on Sunday, July 22, 2012 3:53 PM

@mike wiegand
Mike, I am assuming you are looking at the 'Review' page. Do both browsers display different session variables? If not, hit, CTRL+F5 on one browser to get it a different session id.
BlogHub co-ordinates calls between the client and the server. The Send method in BlogHub are called from JavaScript in the Review page.

Comment posted by
Mark
on Tuesday, July 31, 2012 10:35 AM

Hi Sumit, thanks very much for this detailed tutorial. It was exactly what I needed to get into SignalR.
Best wishes, Mark

Comment posted by
mike
on Tuesday, October 23, 2012 2:36 PM

most bullshit of examples i have ever seen, download it make it work .. i dont know how others say good things about it..
Fuck you Sumit

Comment posted by
Sumit
on Saturday, October 27, 2012 1:56 PM

@mike
I don't see how your suggestion makes the article and better or the discussion any constructive.

Comment posted by
Anton
on Wednesday, October 31, 2012 9:56 AM

Hi Sumit,
Thanks for this wonderful example, however..when I try to run the project which i have created following your tutorial exactly as it is...i get an error on the Index action saying ..."Invalid value for key 'attachdbfilename' "
Please help, as I would love to get it working.
Whe I try to run your example in VS 2010, it says error and your project is incompatible with VS 2010.
Thanks

Comment posted by
Sumit
on Monday, November 5, 2012 7:27 AM

Hello Anton,
Apologies for the delay in response.

Looks like there is an issue with the connection to the default database. To get this going you need to do the following:

1. Determine the correct SQL Server (SQL Express/of LocalDB) name in your environment.
2. Add a connection string to the web.config
3. Use the connection name in BlogPostContext.

Example: Let's assume your SQL Server is at YOURMACHINE\SQLEXPRESS

In such a case your connection string in the web.config will be as follows

In the server-side code: Clients.addMessage should be Clients.All.addMessage in the latest SignalR release (V1 RC2)

Comment posted by
Sumit
on Thursday, February 21, 2013 11:59 AM

SignalR is now Microsoft.AspNet.SignalR . You can install it using

install-package Microsoft.AspNet.SignalR

No more -pre required.

install-package SignalR will no longer work.

Comment posted by
Bhupendra
on Wednesday, April 3, 2013 1:43 AM

Would you help please this is the best article i have seen on SignalR, concern is its not working in IE (I am using IE 8)

Comment posted by
Unfortun8one
on Sunday, October 13, 2013 10:15 AM

Thanks for this example! I've made it work with MVC4 and it will be a great model for future SignalR projects.

Comment posted by
Mayank Gaur
on Monday, December 1, 2014 4:02 AM

Thank you very much for sharing the informative article. It helps me a lot.Thanks again.I have one doubt can I use SignalR to calculate time interval spent by a visitor on a particular page.
Thanks & Regards
Mayank Gaur