Introduction
SignalR is a pretty new and very exciting feature in ASP.NET. It offers a simple and clean API that allows you to create real-time web applications where the server needs to continuously push data to clients. Common applications are chat, news feeds, notifications, and multi-player games.
For this exercise, we're going to build a simple chat application in ASP.NET MVC 4 using the power of SignalR. I presume that you already have the basic knowledge of ASP.NET MVC and how stuff works (for example Model, View, and Controller) in the MVC development approach because the details of them will not be covered in this exercise.
Step 1. Setting up your ASP.NET MVC Project.
To get started, let's proceed and fire up Visual Studio 2012 and create a new project by selecting File > New Project. Under the templates panel, select Visual C# > Web > ASP.NET MVC 4 Web Application. Name your project and click on the OK button.
In the ASP.NET MVC 4 Project Template window, just select ”Basic” since we don't really want to use the built-in forms authentication in this exercise. Then click OK to generate the necessary files for you.
Step 2. Adding SignalR to your Project.
SignalR is available in Visual Studio 2010 (.NET 4.0) and later versions of Visual Studio and can be referenced from Nuget.
Under the Visual Studio Tools tab, select Library Package Manager > Manage Nuget Package for the solution. In the search bar type “Microsoft.Aspnet.SignalR” and hit Enter. The result should provide you with something as in the following.
Now click the Install button to add the SignalR library and its dependencies to your project. Just follow the procedure specified in the wizard until all the necessary files are successfully installed. After the installation you should be able to see the newly added DLLs in your project references folder. See the following:
Okay, we’re now all set and ready to get our hands dirty.
Step 3. Adding a Controller
To add a new controller, just right-click on the Controller’s folder and then select Add > Controller. Name the controller ChatRoomController and then click Add.
Here’s the code of the Controller class.
namespace MvcDemo.Controllers
{
public class ChatRoomController : Controller
{
// GET: /ChatRoom/
public ActionResult SignalRChat()
{
return View();
}
}
}
As you can see, there’s nothing really fancy in the Controller. It just contains the SignalRChat action method that returns a View.
Now let’s proceed and create the SignalRChat view.
Step 4. Adding a View
The first thing we need here is to add a ChatRoom folder within the View folder. The reason for this is that the folder name should match the name of the Controller you’ve created in Step 3. That’s one of the MVC conventions. So for example, if you have a “HomeController”, then you should have a “Home” folder within your View.
Alright, let’s proceed. In your ChatRoom folder add a new view by right-clicking on it and then selecting Add > View. Be sure to name the view “SignaRChat” since that’s the name of the view that the controller must expect. Then click Add to generate the View.
Just for the simplicity of this exercise, I just set it up like this.
<div id="container">
<input type="hidden" id="nickname" />
<div id="chatlog"></div>
<div id="onlineusers">
<b>Online Users</b>
</div>
<div id="chatarea">
<div class="messagelog">
<textarea spellcheck="true" id="message" class="messagebox"></textarea>
</div>
<div class="actionpane">
<input type="button" id="btnsend" value="Send" />
</div>
<div class="actionpane">
<select id="users">
<option value="All">All</option>
</select>
</div>
</div>
<div id="dialog" title="Enter your name to start a chat.">
<input type="text" id="nick" />
</div>
</div>
Step 5. Adding a Hub
To provide you with a quick overview, the Hub is the centerpiece of the SignalR. Similar to the Controller in ASP.NET MVC, a Hub is responsible for receiving input and generating the output to the client.
Before we create a Hub, let’s add a new folder first within the root of the project. In this exercise, I named the folder Hubs. Right-click on that folder and select Add > ASP.NET SignalR Hub class. Name the class “ChatHub” and then click OK to generate the file.
Here’s the code block of the Hub class.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.SignalR;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace MvcDemo.Hubs
{
public class ChatHub : Hub
{
static ConcurrentDictionary<string, string> dic = new ConcurrentDictionary<string, string>();
public void Send(string name, string message)
{
Clients.All.broadcastMessage(name, message);
}
public void SendToSpecific(string name, string message, string to)
{
Clients.Caller.broadcastMessage(name, message);
Clients.Client(dic[to]).broadcastMessage(name, message);
}
public void Notify(string name, string id)
{
if (dic.ContainsKey(name))
{
Clients.Caller.differentName();
}
else
{
dic.TryAdd(name, id);
foreach (KeyValuePair<string, string> entry in dic)
{
Clients.Caller.online(entry.Key);
}
Clients.Others.enters(name);
}
}
public override Task OnDisconnected()
{
var name = dic.FirstOrDefault(x => x.Value == Context.ConnectionId.ToString());
string s;
dic.TryRemove(name.Key, out s);
return Clients.All.disconnected(name.Key);
}
}
}
As you may have noticed, the ChatHub class inherits from Microsoft.Aspnet.SignalR.Hubs.Hub. Keep in mind that public methods defined within the Hub class are intended to be called from the client code (JavaScript). They actually result in a response being sent to the client.
At runtime, a JavaScript file is generated dynamically that contains the client-side implementations of the public methods defined in the Hub. These client-side methods will act as a proxy to the server-side methods. You write an implementation of public methods from the Hub in your client-side code so that the Hub can call it and thereby provide the response.
The dictionary is where we store the list of available users that have joined the chat room. I used a dictionary so we can easily add and remove items from it using key/value pairs.
The Send() method broadcasts the message to the users by passing the name and the message as the parameters. The SendToSpecific() method on the other hand broadcasts the message to a specific user. The Notify() method tells the client when someone enters the chat room and then adds them to the list of the available users. The OnDisconnected() method handles the removal of users if someone leaves the chat room. This method is asynchronous and will be called whenever the connection from the client is closed (for example closing the browser).
Step 6. Implementing the Client-Side code
Now that we already have our Hub setup we can now proceed with the client-side implementation of our chat application.
Add the following code block below in your View (.cshtml/.aspx).
@section scripts {
@Scripts.Render("~/Scripts/jquery-ui-1.9.2.min.js")
@Scripts.Render("~/Scripts/jquery.signalR-1.0.1.min.js")
@Scripts.Render("/signalr/hubs")
<script type="text/javascript">
$(function () {
showModalUserNickName();
});
function showModalUserNickName() {
$("#dialog").dialog({
modal: true,
buttons: {
Ok: function () {
$(this).dialog("close");
startChatHub();
}
}
});
}
function startChatHub() {
var chat = $.connection.chatHub;
// Get the user name.
$('#nickname').val($('#nick').val());
chat.client.differentName = function (name) {
showModalUserNickName();
return false;
// Prompts for different user name
$('#nickname').val($('#nick').val());
chat.server.notify($('#nickname').val(), $.connection.hub.id);
};
chat.client.online = function (name) {
// Update list of users
if (name == $('#nickname').val())
$('#onlineusers').append('<div class="border" style="color:green">You: ' + name + '</div>');
else {
$('#onlineusers').append('<div class="border">' + name + '</div>');
$("#users").append('<option value="' + name + '">' + name + '</option>');
}
};
chat.client.enters = function (name) {
$('#chatlog').append('<div><i>' + name + ' joins the conversation</i></div>');
$("#users").append('<option value="' + name + '">' + name + '</option>');
$('#onlineusers').append('<div class="border">' + name + '</div>');
};
// Create a function that the hub can call to broadcast chat messages.
chat.client.broadcastMessage = function (name, message) {
// Interpret smileys
message = message.replace(":)", "<img src=\"/images/smile.gif\" class=\"smileys\" />");
message = message.replace(":D", "<img src=\"/images/laugh.gif\" class=\"smileys\" />");
message = message.replace(":o", "<img src=\"/images/cool.gif\" class=\"smileys\" />");
// Display the message
$('#chatlog').append('<div class="border"><span style="color:orange">' + name + '</span>: ' + message + '</div>');
};
chat.client.disconnected = function (name) {
// Calls when someone leaves the page
$('#chatlog').append('<div><i>' + name + ' leaves the conversation</i></div>');
$('#onlineusers div').remove(":contains('" + name + "')");
$("#users option").remove(":contains('" + name + "')");
};
// Start the connection.
$.connection.hub.start().done(function () {
// Calls the notify method of the server
chat.server.notify($('#nickname').val(), $.connection.hub.id);
$('#btnsend').click(function () {
if ($("#users").val() == "All") {
// Call the Send method on the hub.
chat.server.send($('#nickname').val(), $('#message').val());
}
else {
chat.server.sendToSpecific($('#nickname').val(), $('#message').val(), $("#users").val());
}
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
}
</script>
}
At the very top within the @RenderSection, you need to define a reference to jQueryUI and SignalR scripts. Please note that the script reference should be in the following order.
- jQuery Script
- jQueryUI
- SignalR script
- SignalR/Hubs
- Your custom-defined script
You might be wondering why we haven’t included a reference to the jQuery script in the code above. The reason for this is that when you created the Template, the engine automatically added a reference to the jQuery script within your _Layout.cshml before the closing tag of the <body> element and automatically bundled it for you for page performance.
Note. Adding a reference to the jQuery script within your View will cause an error when running your application that uses SignalR. This is because the reference to the jQuery script will be duplicated and the order of the script references will be changed and that can cause the SignalR script to throw an exception.
The showModalUserNickName() function calls the jQueryUI dialog. This function will be called when the browser is loaded to prompt the user to enter their name before joining the chat room. After entering their name the startChatHub() function will be called. This function is where the actual implementation of the chat is defined. The logic is that it first creates a proxy connection to the Hub (ChatHub). Once connected, we then have access to the public methods defined in our Hub class. The next step is to get the name of the user. If the user already exists in the dictionary then they will be prompted to enter another name. Then it will call the Notify() method to add the new user to the dictionary and notify other users that someone has joined the chat room. The next function updates the message logs, available users, and the online user’s panel when someone joins the room. Then it calls the broadcastMessage() function to display the messages among users in the message logs. You will also see that I’ve added some code within that function that will interpret smileys. The client. disconnected() function is responsible for updating the UI and the list of users when someone leaves the chat room. And finally, call the hub. start() function to start the connection between the client and the hub (server). This method contains the implementation of sending the message to other users or specific users.
Step 7. Wrapping Up
- Adding a new CSS file: Create a new CSS file under the Content folder. Name it “chat.css”. Then add the following style definitions to the CSS file.
#container {
width: 400px;
margin-left: auto;
margin-right: auto;
}
#chatlog {
width: 250px;
background-color: aliceblue;
float: left;
}
#onlineusers {
float: right;
width: 100px;
background-color: #D4D4D4;
}
#chatarea {
clear: both;
width: 200px;
}
#chatarea .messagelog {
float: left;
height: 90%;
top: 10%;
position: relative;
}
#chatarea .messagelog .messagebox {
width: 400px;
height: 80%;
}
#chatarea .actionpane {
position: relative;
float: left;
}
.smileys {
width: 14px;
height: 14px;
}
- Adding Smiley images: Create a new folder within the root project and name it “Images”. Then add your smileys within that folder.
- Referencing CSS files: Then add the following references at the <head> section in your _Layout.cshtml.
<link href="~/Content/chat.css" rel="stylesheet" />
<link href="~/Content/themes/base/jquery-ui.css" rel="stylesheet" />
- Modifying the default Route: Update the default route under App_Start > RouteConfig so that it will automatically display the SignalRChat page when you run the application. Here’s the updated code.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new
{
controller = "ChatRoom",
action = "SignalRChat",
id = UrlParameter.Optional
}
);
- Registering the MapHubs Route: And finally, the most important thing is to make things work. Add the following line below in your Global.asax.cs under the Application_Start event.
RouteTable.Routes.MapHubs();
Note. Be sure to define the MapHubs() route before the RegisterRoutes() call.
Step 8. Run the Page
Here’s the output below.
That’s it! I hope someone finds this article useful!