Real-Time Web Enablement with SignalR in .Net (Part II)

Before you start reading this Article, I suggest you to go through our first article in the series, Real-Time Web enablement with SignalR in .Net (Part I), in order to set the context right. It talks about SignalR and the general idea of its functionality and usability. In this installment we will look at the programming aspect of the usability scenarios and how to use it in real life setups.

Getting Started with SignalR

Your app should be on .Net Framework 4.0 or higher in order to use SignalR. You can find it on NuGet. To install SignalR in your .Net Application, click on Tools | Library Package Manager | Package Manager Console in your VS IDE and run the command:

install-package Microsoft.AspNet.SignalR

This command will add Server and Client side libraries in your application, making you ready to go.

Add Server and Client side libraries
Add Server and Client side libraries

Follow these steps to get hands on with SignalR with one simple example.

Adding Reference to SignalR

Add reference to SignalR in your class file.

using Microsoft.AspNet.SignalR;

Implementing the Endpoint

The endpoint, or SignalR service, can be implemented using persistent connection or hubs. I will be implementing Hubs in the example.

Example Code

Route Registration

Once the endpoint is implemented, we must register it in the routing system, which will allow us to access it. The best place to do this is in the Application_Start() of Global.asax, so that it runs during initialization of the application.

Example Code

Implementing the Web Client

Implementing the web client begins by including a reference to the client library of this component on our page or view. In order to support older clients that do not natively support JSON deserialization, you will have to add a reference to the json2.js script library before referencing SignalR related script files.

Example Code

Initiate the Connection

To initiate a connection from client to server, you need to start the hub in script. Along with initiating connection you would want to define the event that triggers the server push; it could be anything from a button click or some status change. To keep it simple, in the example it’s a button click on your page.

Example Code

Defining Client Procedure to be Invoked by Server

If you recall from the “Implementing the endpoint” step, I had called a method broadcastMessage ()on all the clients. The definition of this method is done in the script at the client side as shown below.

Example Code

 Now run the application in multiple browser windows and click at the button control on one page. This will display “Hello <User  Name> Welcome to Devx” in <YOUR HTML CONTROL> in opened browser windows, even for those that were not active.

So far so good, without worrying about the rawness of transport and real time web intricacies, you made your site RealTime web enabled in no time with SignalR. But is that all you wanted for your site, if not…then read on.

Unraveling Real World Problems with SignalR

Applications in the real world are not as easy as the example we saw just now. To implement real-time web with SignalR, you will have to play around with the connection ids of connected clients. And if authorization is enabled for a web site then you will have to write your own code and save the connection id along with logged in user details, as authorization is not offered here out of the box. Today authorization is more or less an integral part of web sites, and I am not taking up some unexplored topic here. Nevertheless, I would like to detail different strategies to help you choose the best one for your requirement.

Diverse Approaches for Managing Connection IDs

  • DEPRECATED: In its initial releases, SignalR provided a way to override its Connection Factory where a programmer could assign a unique connection id to each connected client and that could be used while invoking the client method; however it is no longer supported.
    http://stackoverflow.com/questions/9606569/signalr-set-clientid-manually
  • Now you will have to save the connection id along with the unique key of the authorized user in order to enable your code to invoke the method on the selected client. A very commonly used design is to maintain a user list on the server that plots connection IDs to users and registers the users as they connect and disconnect1, as proposed at the link http://forums.asp.net/t/1887454.aspx/1?How+to+manually+set+the+connection+id.
  • For application sanity and keeping the app server memory free for processing, I personally suggest using DB tales instead of adding things up in some variable at the app server. The above mentioned concept can be implemented using a DB table we well. I will elaborate this approach in article and at the end suggest some enhancements based on the requirement.

Step 1: Boot up

Here I assume that:

1. You have installed SignalR lib in your .Net application, on framework 4.0 or higher(ref).

2. You have defined your end point by deriving your class from Hub (ref).

3. You have made the necessary changes to your Global.asax (ref).

Step 2: Change in DB Schema

Create one db table to save logged in user’s id along with connection id. Entity class would look something like what is shown below.

Example Code

Step 3: Client and Connection

After including references to the SignalR client library as we did in previous example, you will have to initiate connection. When a client connects to Hub, you can invoke the method to save user information along with connection id. So here we write a client method, which is invoked when the master page or frame window loads.

Example Code

Heremarkonline() is a server side method in your Hub class, to store User and connection id info in the table.

Example Code

Once we are done with “Setting up the connection,” which includes saving rows to db schema, we should implement logic to clear the table as and when the user disconnects from application.

Now disconnection can happen in two manners. The user can close the browser or can log off. To handle both scenarios, you can write methods similar to those shown below.

In the class that derives from Hub, override onDisconnected().

Example Code

And when a user logs off from the application, invoke a method similar to the one shown below.

Example Code

Step 4: Updating DB Schema

Code for updateOnlineUser()method is shown below.

Example Code

Step 5: Fetching a User’s Connection ID/s

Now write a code that fetches all connection ids for a specific user.

Example Code

Step 6: Client-Server BI Directional RPC

Now whenever a server method is invoked, you can fetch list of available connection ids for the target user/s and invoke the method on available connection ids.

Example Code

Where broadcastMessage is a client side method defined as:

Example Code

The above mentioned code will work even If you have multiple connection id scenarios like notification, chat and Co-Create on the same screen, working parallel to each other.

Multiple connection id scenarios
Multiple connection id scenarios

How to Make Multiple Connection ID Scenario More Scalable

Until now we were fetching all connection ids for a user and invoking client method on all of these. This way, methods meant for Chat would be called upon frame for co create or notification as well, which eventually slows down the application with unnecessary processing.

To make it better, you can store the origin of connection id along with it. When invoking markonline(), you can send a string say “source” as a parameter. For example from the Co-Create frame as markonline(“cocreate”) and from Chat window  markonline(“chat”)  and from the master page markonline(“master”) ; this will make your table look like this:

username

connectionid

Source

User1

24b11640-XXXX-4069-XX96-8389223418ac

cocreate

User1

eaa2de3d-f7df-46ce-XXXX-32bdfe612794

master

User2

8d2XXcc5-XX61-4f8d-a9f4-d3bcXXX1f14c

cocreate

User2

3b900a78-4c1e-4XX8-be2c-XXXXXXXX

chat

User3

XXXXXX-af57-4cc0-a2c0-4075947e3915

cocreate

User3

7f946c23-XXXX-4b49-b7d8-ee97cac5eXXX

chat

User4

b2136fe5-XXXX-48d3-XXXX-047b77f34b09

master

……

……

……

Now while fetching the list of connection ids from the table with isUserOnline(string username) method, you can specify source based on the method you are invoking it for by passing it as a where clause to the query. So when you want connection id for chat methods, you will fetch record only for source=”chat”.

How to Enhance Same Code for Multiple Sessions of Same User

Today a user can run the application from multiple devices as well. To make the above approach work for multiple session scenarios, you can store session id along with userid, source and connection id and fetch the result based on these parameters. This will ensure that when you invoke client method from the server, it reaches the destination it is meant to reach. Similarly, while deleting records when a user logs off or closes a browser window, you delete rows belonging to that session only.

Perspective

As I said earlier, SignalR and its usability looks very promising, and you saw how you can embed real time web in the application without getting your hands dirty. Yes, there are tricks, which you would have to play while working on selected list of connection ids however that is restricted to some “ifs” here and some “where” there but saves us from dealing with the rawness of communication APIs.

For me it’s a Like for SignalR.

References

1. ASP.Net Forums. “How to manually set the connection id”.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read