A basic understanding of C# and jQuery is needed to follow this tutorial. Communication in our current age is largely digital, and the most popular form of digital communication is Instant Messaging. Some applications include some form of chat implementation e.g. Slack or Facebook. In this tutorial, we will consider how to build a chat application using C# .NET. To follow along with this tutorial, you will require: Visual Studio, an IDE popularly used for building .NET projects. View installation details . here Basic knowledge of C#. Basic knowledge of .NET MVC. Basic knowledge of JavaScript (jQuery). Setting up our chat project Using our Visual Studio IDE, we’ll create our chat project by following the wizard. New Project We will: Set C# as our language to use. Select .NET MVC Project as the template. Fill in the Project name e.g. HeyChat. Fill in the Solution name i.e. application name. Creating our chat app Defining pages and routes For the purpose of this tutorial, our chat app will consist of 2 pages: The front page — where our user signs up. The chat view — where our user selects a contact and exchanges messages. To achieve these views, we will need the following routes: The route to render the front page. The route to implement login. The route to render the chat page. 💡 These routes only render the views and implement user login. We’ll add more routes as we go along. Adding these routes to our file we’ll have: RouteConfig.cs routes.MapRoute( name: "Home", url: "", defaults: new { controller = "Home", action = "Index" } ); routes.MapRoute( name: "Login", url: "login", defaults: new { controller = "Auth", action = "Login" } ); routes.MapRoute( name: "ChatRoom", url: "chat", defaults: new { controller = "Chat", action="Index"} ); These route definitions specify the route pattern and the and to handle it. Controller Action 💡 Creating our project with Visual Studio automatically creates the _HomeContoller.cs_ file with an _Index_ action. We will use this for our home route. In our we’ll render the front page where our users can log in with: HomeController.cs //HomeController.cs // ... Using System.Web.Mvc; // ... public class HomeController : Controller { public ActionResult Index() { if ( Session["user"] != null ) { return Redirect("/chat"); } return View(); } } 💡 The _View_ function creates a view response which we return. When it is invoked, C# looks for the default view of the calling controller class. This default view is the _index.cshtml_ file found in the Views directory, in a directory with the same name as the Controller i.e. The default view of the HomeController class will be the _Views/Home/index.cshtml_ file. Setting up our database In order to implement our login feature, we’ll need a database to store users. There are several database drivers to choose from but, in this tutorial, we’ll use the MySQL database driver along with a .NET ORM called Entity Framework. We will start by installing the package via NuGet (.NET’s package manager). And then, we’ll install the package also via NuGet, to provide us with our ORM functionality. MySql.Data.Entities Entity Framework 💡 To install packages using NuGet, right-click the Packages folder in our project solution; select the _Add Package_ option; and search and select your desired package. Once our packages have been installed, we will begin setting up our database connection and communication. First, we will add our database connection credentials to the file found in our solution folder. In we will add: Web.config Web.config <connectionStrings> <add name="YourConnectionName" connectionString="Server=localhost;Database=database_name;Uid=root;Pwd=YourPassword;" providerName="MySql.Data.MySqlClient" /> </connectionStrings> ⚠️ You will need to replace the placeholder values in the snippet above with actual values database values. The file is an file and the above element will be added in the body of the element of the file. Web.config XML connectionStrings configuration Next, we’ll create a folder inside our solution folder (on the same folder level as ). In this folder, we will create our model class - this class is a representation of our table. For the login feature we will create the file. In this class file, we will add the properties of our model: Models Controllers User.cs // File: User.cs file using System; using System.Collections.Generic; namespace HeyChat.Models { public class User { public User() { } public int id { get; set; } public string name { get; set; } public DateTime created_at { get; set; } } } 💡 To create a model class, right-click the Model folder, select the _Add_ and _New File_ options, and then _Empty Class_ option filling in the class name. Our model defines an ID for unique identification, user’s name and created date of the user for our users table. User Finally, we will add our database context class. This class reads in the database connection configuration we defined in the file and takes the Model classes (Datasets) to which it should apply the configuration. Web.config We will create our context class in our folder, following the same steps of creating a new empty class, and we will name it . In it, we will add the following: Models ChatContext.cs // File: ChatContext.cs using System; using System.Data.Entity; namespace HeyChat.Models { public class ChatContext: DbContext { public ChatContext() : base("YourConnectionName") { } public static ChatContext Create() { return new ChatContext(); } public DbSet<User> Users { get; set; } } } 💡 We are implementing the Entity Framework ORM using the Code First method. This method involves writing the code defining our models (tables) without any existing database or tables. With this method, the database and tables will be created when our application code is executed. Logging in our users Since our database connection and model (though as we go along more models may be introduced) have been created, we can proceed with our login functionality. The front page rendered from the will consist of a form that accepts a user’s name. This form will be submitted to the login AuthController Login` action method. HomeController /` route which we defined earlier. Following our route definition, this request will be handled by the and its We will create the class and add our code for storing or retrieving a user’s details. The option to either store or retrieve will be based on if the user’s name already exists in our Table. The code for the is below: AuthController Users AuthController // File: AuthController // ... using HeyChat.Models; public class AuthController : Controller { [HttpPost] public ActionResult Login() { string user_name = Request.Form["username"]; if (user_name.Trim() == "") { return Redirect("/"); } using (var db = new Models.ChatContext()) { User user = db.Users.FirstOrDefault(u => u.name == user_name); if (user == null) { user = new User { name = user_name }; db.Users.Add(user); db.SaveChanges(); } Session["user"] = user; } return Redirect("/chat"); } } In the code above, we check if a user exists using the name. If it exists we retrieve the user’s details and, if it doesn’t, we create a new record first. Then we assign the user’s details into a object for use throughout the application. Lastly, we redirect the user to the chat page. session Rendering the chat page One feature of most Chat applications is the ability to choose who to chat with. For the purpose of this tutorial, we will assume all registered users can chat with each other so our chat page will offer the possibility of chatting with any of the users stored in our database. Earlier, we defined our chat route and assigned it to the class and its action method. ChatController Index Let’s create the and implement the rendering of the chat page with available contacts. Paste the code below into the : ChatController ChatController // File: ChatController // ... using HeyChat.Models; namespace HeyChat.Controllers { public class ChatController : Controller { public ActionResult Index() { if (Session["user"] == null) { return Redirect("/"); } var currentUser = (Models.User) Session["user"]; using ( var db = new Models.ChatContext() ) { ViewBag.allUsers = db.Users.Where(u => u.name != currentUser.name ) .ToList(); } ViewBag.currentUser = currentUser; return View (); } } } To get the available contacts, we read all the users in our database except the current user. These users are passed to our client side using . We also pass the current user using . ViewBag ViewBag Now that we have retrieved all the available contacts into the object, we will create the markup for displaying these contacts and the rest of the chat page to the user. To create the view file for our chat page, we create a folder in the folder. ViewBag Chat Views Next, right click the folder, select the options to → , select the Razor template engine and name the file . Paste in the code below into the file: Chat Add Views index.cshtml <!DOCTYPE html> <html> <head> <title>pChat &mdash; Private Chatroom</title> <link rel="stylesheet" href="@Url.Content("~/Content/app.css")"> </head> <body> <!-- Navigation Bar --> <nav class="navbar navbar-inverse"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="#">pChat - @ViewBag.currentUser.name </a> </div> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Log Out</a></li> </ul> </div> </nav> <!-- / Navigation Bar --> <div class="container"> <div class="row"> <div class="col-xs-12 col-md-3"> <aside class="main visible-md visible-lg"> <div class="row"> <div class="col-xs-12"> <div class="panel panel-default users__bar"> <div class="panel-heading users__heading"> Contacts (@ViewBag.allUsers.Count) </div> <div class="__no__chat__"> <p>Select a contact to chat with</p> </div> <div class="panel-body users__body"> <ul id="contacts" class="list-group"> @foreach( var user in @ViewBag.allUsers ) { <a class="user__item contact-@user.id" href="#" data-contact-id="@user.id" data-contact-name="@user.name"> <li> <div class="avatar"> <img src="@Url.Content("~/Content/no_avatar.png")"> </div> <span>@user.name</span> <div class="status-bar"></div> </li> </a> } </ul> </div> </div> </div> </div> </aside> </div> <div class="col-xs-12 col-md-9 chat__body"> <div class="row"> <div class="col-xs-12"> <ul class="list-group chat__main"> </ul> </div> <div class="chat__type__body"> <div class="chat__type"> <textarea id="msg_box" placeholder="Type your message"></textarea> <button class="btn btn-primary" id="sendMessage">Send</button> </div> </div> <div class="chat__typing"> <span id="typerDisplay"></span> </div> </div> </div> </div> </div> <script src="@Url.Content("~/Content/app.js")"></script> </body> </html> 💡 `@Url.Content (“~/Content/app.css”) _and_ @Url.Content("~/Content/app.js") _load some previously bundled JavaScript and CSS dependencies such as jQuery and Bootstrap from our_ Content` folder. In our view file, we create a sidebar and loop through the users passed to to indicate the contacts available using Razor’s directive. We also add a text area to type and send messages to these contacts. ViewBag @foreach Selecting contacts and sending messages When our user selects a contact to chat with, we would like to retrieve the previous messages between the user and the selected contact. In order to achieve this, we would need a table for storing messages between users and a Model for this table. Let’s create a model called in the folder. It will consist of a unique , , , , and the date. The code for the model is below: Conversations Models id sender_id receiver_id message status created_at // File: Conversation.cs using System; namespace HeyChat.Models { public class Conversation { public Conversation() { status = messageStatus.Sent; } public enum messageStatus { Sent, Delivered } public int id { get; set; } public int sender_id { get; set; } public int receiver_id { get; set; } public string message { get; set; } public messageStatus status { get; set; } public DateTime created_at { get; set; } } } After creating the model, we will add it to the file as seen below: Conversation ChatContext // File: ChatContext.cs using System; using System.Data.Entity; namespace HeyChat.Models { public class ChatContext: DbContext { public ChatContext() : base("MySqlConnection") { } public static ChatContext Create() { return new ChatContext(); } public DbSet<User> Users { get; set; } public DbSet<Conversation> Conversations { get; set; } } } To retrieve the messages, we will create a route for /conversations/{contact}`. This route will accept a contact ID, retrieve messages between the current user and the contact, then return the messages in a JSON response. /contact` It will be handled by the in the action method as seen below: ChatController ConversationWithContact //ChatController.cs ... public JsonResult ConversationWithContact(int contact) { if (Session["user"] == null) { return Json(new { status = "error", message = "User is not logged in" }); } var currentUser = (Models.User)Session["user"]; var conversations = new List<Models.Conversation>(); using (var db = new Models.ChatContext()) { conversations = db.Conversations. Where(c => (c.receiver_id == currentUser.id && c.sender_id == contact) || (c.receiver_id == contact && c.sender_id == currentUser.id)) .OrderBy(c => c.created_at) .ToList(); } return Json( new { status = "success", data = conversations }, JsonRequestBehavior.AllowGet ); } Now that we have a route to retrieve old messages, we will use some jQuery to select the user, fetch the messages and display them on our page. In our view file, we will create a tag to hold our JavaScript and jQuery functions. In it, we’ll add: script ... <script> let currentContact = null; // Holds current contact let newMessageTpl = `<div> <div id="msg-{{id}}" class="row __chat__par__"> <div class="__chat__"> <p>{{body}}</p> <p class="delivery-status">Delivered</p> </div> </div> </div>`; ... // select contact to chat with $('.user__item').click( function(e) { e.preventDefault(); currentContact = { id: $(this).data('contact-id'), name: $(this).data('contact-name'), }; $('#contacts').find('li').removeClass('active'); $('#contacts .contact-' + currentContact.id).find('li').addClass('active'); getChat(currentContact.id); }); // get chat data function getChat( contact_id ) { $.get("/contact/conversations/" + contact_id ) .done( function(resp) { var chat_data = resp.data || []; loadChat( chat_data ); }); } //load chat data into view function loadChat( chat_data ) { chat_data.forEach( function(data) { displayMessage(data); }); $('.chat__body').show(); $('.__no__chat__').hide(); } function displayMessage( message_obj ) { const msg_id = message_obj.id; const msg_body = message_obj.message; let template = $(newMessageTpl).html(); template = template.replace("{{id}}", msg_id); template = template.replace("{{body}}", msg_body); template = $(template); if ( message_obj.sender_id == @ViewBag.currentUser.id ) { template.find('.__chat__').addClass('from__chat'); } else { template.find('.__chat__').addClass('receive__chat'); } if ( message_obj.status == 1 ) { template.find('.delivery-status').show(); } $('.chat__main').append(template); } Now that selecting a contact retrieves previous messages, we need our user to be able to send new messages. To achieve this, we will create a route that accepts the message being sent and saves it to the database, and then use some jQuery to read the message text from the field and send to this route. textarea //RouteConfig.cs ... routes.MapRoute( name: "SendMessage", url: "send_message", defaults: new { controller = "Chat", action = "SendMessage" } ); As specified in the file, this route will be handled by the action method of the . RouteConfig SendMessage ChatController //ChatController.cs ... [HttpPost] public JsonResult SendMessage() { if (Session["user"] == null) { return Json(new { status = "error", message = "User is not logged in" }); } var currentUser = (User)Session["user"]; string socket_id = Request.Form["socket_id"]; Conversation convo = new Conversation { sender_id = currentUser.id, message = Request.Form["message"], receiver_id = Convert.ToInt32(Request.Form["contact"]) }; using ( var db = new Models.ChatContext() ) { db.Conversations.Add(convo); db.SaveChanges(); } return Json(convo); } Adding realtime functionality There are several features of a chat application that require realtime functionality, some of which are: Receiving messages sent in realtime. Being notified of an impending response — the ‘user is typing’ feature. Getting message delivery status. Instant notification when a contact goes offline or online. In achieving these features, we will make use of . To proceed lets head over to the Pusher and create an app. You can if you haven’t got an account. Fill out the create app form with the information requested. Next, we’ll install the package in our C# code using NuGet. Pusher dashboard register for free Pusher Server To achieve some of our stated realtime features, we will need to be able to trigger events on the client side. In order to trigger client events in this application, we will make use of Private Channels. We will create our private channel when a contact is chosen. This channel will be used to transmit messages between the logged in user and the contact he is sending a message to. Private channels require an authentication endpoint from our server side code to be available, because when the channel is instantiated Pusher will try to authenticate that the client has valid access to the channel. The default route for Pusher’s authentication request is , so we will create this route and implement the authentication. /pusher/auth First in our file we will add the route definition: RouteConfig.cs routes.MapRoute( name: "PusherAuth", url: "pusher/auth", defaults: new { controller = "Auth", action = "AuthForChannel"} ); Then, as we have defined above, in the class file we will create the action method and add: AuthController AuthForChannel public JsonResult AuthForChannel(string channel_name, string socket_id) { if (Session["user"] == null) { return Json(new { status = "error", message = "User is not logged in" }); } var currentUser = (Models.User)Session["user"]; var options = new PusherOptions(); options.Cluster = "PUSHER_APP_CLUSTER"; var pusher = new Pusher( "PUSHER_APP_ID", "PUSHER_APP_KEY", "PUSHER_APP_SECRET", options); if (channel_name.IndexOf(currentUser.id.ToString()) == -1) { return Json( new { status = "error", message = "User cannot join channel" } ); } var auth = pusher.Authenticate(channel_name, socket_id); return Json(auth); } Our authentication endpoint, above, takes the name of the channel and the socket ID of the client, which are sent by Pusher at a connection attempt. 💡 We will name our private channels using the IDs of the participants of the conversation i.e. the sender and receiver. This we will use to restrict the message from being broadcast to other users of the Messenger app that are not in the specific conversation. Using the .NET library, we authenticate the user by passing the channel name and socket ID. Then we return the resulting object from authentication via JSON. PusherServer For more information on client events and private channels, kindly check out the Pusher . documentation 💡 Client events can only be triggered by private or presence channels. In the script section of our view, we will instantiate the variable for our private channel. We will also adjust our contact selecting snippet to also create the channel for sending messages, typing and delivery notifications: ... <script> ... let currentContact = null; // Holds contact currently being chatted with let socketId = null; let currentconversationChannel = null; let conversationChannelName = null; //Pusher client side setup const pusher = new Pusher('PUSHER_APP_ID', { cluster:'PUSHER_APP_CLUSTER' }); pusher.connection.bind('connected', function() { socketId = pusher.connection.socket_id; }); // select contact to chat with $('.user__item').click( function(e) { e.preventDefault(); currentContact = { id: $(this).data('contact-id'), name: $(this).data('contact-name'), }; if ( conversationChannelName ) { pusher.unsubscribe( conversationChannelName ); } conversationChannelName = getConvoChannel( (@ViewBag.currentUser.id * 1) , (currentContact.id * 1) ); currentconversationChannel = pusher.subscribe(conversationChannelName); bind_client_events(); $('#contacts').find('li').removeClass('active'); $('#contacts .contact-' + currentContact.id).find('li').addClass('active'); getChat(currentContact.id); }); function getConvoChannel(user_id, contact_id) { if ( user_id > contact_id ) { return 'private-chat-' + contact_id + '-' + user_id; } return 'private-chat-' + user_id + '-' + contact_id; } function bind_client_events(){ //bind private channel events here currentconversationChannel.bind("new_message", function(msg) { //add code here }); currentconversationChannel.bind("message_delivered", function(msg) { $('#msg-' + msg.id).find('.delivery-status').show(); }); } We have also saved the used to connect to the channel in a variable. This will come in handy later. socket_id Receiving messages sent in realtime Earlier, we added a route to save messages sent as conversations between the user and a contact. However, after these messages are saved, we would like the messages to be added to the screen of both the user and contact. For this to work, in our C# code, after storing the message we will trigger an event via our Pusher private channel. Our clients will then listen to these events and respond to them by adding the messages they carry to the screen. In our class file, after saving the conversation we will add the following: ChatController private Pusher pusher; //class constructor public ChatController() { var options = new PusherOptions(); options.Cluster = "PUSHER_APP_CLUSTER"; pusher = new Pusher( "PUSHER_APP_ID", "PUSHER_APP_KEY", "PUSHER_APP_SECRET", options ); } [HttpPost] public JsonResult SendMessage() { if (Session["user"] == null) { return Json(new { status = "error", message = "User is not logged in" }); } var currentUser = (User)Session["user"]; string socket_id = Request.Form["socket_id"]; Conversation convo = new Conversation { sender_id = currentUser.id, message = Request.Form["message"], receiver_id = Convert.ToInt32(Request.Form["contact"]) }; using ( var db = new Models.ChatContext() ) { db.Conversations.Add(convo); db.SaveChanges(); } var conversationChannel = getConvoChannel( currentUser.id, contact); pusher.TriggerAsync( conversationChannel, "new_message", convo, new TriggerOptions() { SocketId = socket_id }); return Json(convo); } private String getConvoChannel(int user_id, int contact_id) { if (user_id > contact_id) { return "private-chat-" + contact_id + "-" + user_id; } return "private-chat-" + user_id + "-" + contact_id; } To make use of the Pusher server-side functionality, we will add to the top of our controller file. using PusherServer; 💡 We have accepted the _socket_id_ from the user when sending the message. This is so that we can specify that the sender is exempted from listening to the event they broadcast. In our view, we will listen to the event and use this to add the new message to our view. new_message //index.cshtml ... <script> ... //Send button's click event $('#sendMessage').click( function() { $.post("/send_message", { message: $('#msg_box').val(), contact: currentContact.id, socket_id: socketId, }).done( function (data) { //display the message immediately on the view of the sender displayMessage(data); $('#msg_box').val(''); }); }); function bind_client_events(){ //listening to the message_sent event by the message's recipient currentconversationChannel.bind("new_message", function(msg) { if ( msg.receiver_id == @ViewBag.currentUser.id ) { displayMessage(msg); } }); } Implementing the typing indicator feature This feature makes users aware that the conversation is active and a response is being typed. To achieve it, we will listen to the event of our message text area and, upon the occurrence of this event, we will trigger a client event called . keyup keyup client-is-typing // index.cshtml function bind_client_events(){ currentconversationChannel.bind("client-is-typing", function(data) { if ( data.user_id == currentContact.id && data.contact_id == @ViewBag.currentUser.id ) { $('#typerDisplay').text( currentContact.name + ' is typing...'); $('.chat__typing').fadeIn(100, function() { $('.chat__type__body').addClass('typing_display__open'); }).delay(1000).fadeOut(300, function(){ $('.chat__type__body').removeClass('typing_display__open'); }); } }); ... } //User is typing var isTypingCallback = function() { chatChannel.trigger("client-is-typing", { user_id: @ViewBag.currentUser.id, contact_id: currentContact.id, }); }; $('#msg_box').on('keyup',isTypingCallback); ... Conclusion We have built a chat application with some of its basic features in C# with the help of jQuery, and have also implemented some of the common realtime features present in chat applications using Pusher. This post was first published to . Pusher