Send periodic response from the server using SignalR in ASP.NET MVC

Rajnilari2015
Posted by in SignalR category on for Intermediate level | Points: 250 | Views : 442 red flag

SignalR is the technology by which we can add real time web functionality to applications. That means, the server pushes the content/data to the connected clients instantaneously without the server waiting for the client for making a new data request. In this article, we will look into an interesting problem of sending periodic response from the server for just one client invocation.


 Download source code for Send periodic response from the server using SignalR in ASP.NET MVC

Introduction

SignalR is the technology by which we can add real time web functionality to applications. That means, the server pushes the content/data to the connected clients instantaneously without the server waiting for the client for making a new data request.

In the previous article, we have seen how to pass messages / notification from SignalR hub. In this article, we will look into an interesting problem of sending periodic response from the server for just one client invocation.

Problem Statement

Client sends a request to the server. As soon as the Server gets the request, it sends a message to the client "Validating Request". After a delay of say 300 milliseconds, the server will send message to the client as "Processing Request".Then after 200 milliseconds, server send a message as "Preparing Output". Finally the result will be displayed to the client.

Straight to Experiment

This experiment was done on top of the previous one which is shared here. So, we will not describe everything from the scratch rather will demonstrate only those changes that are needed to perform the experiment.

Changes in HomeController.cs file

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Hosting;
using System.Web.Mvc;
using WebApplication1.Models;

namespace WebApplication1.Controllers
{
    public class HomeController : Controller
    {
        List<Employee> empList;

        //Fetch Employee Records
        public ActionResult Index()
        {
            return View();
        }
        [HttpGet]
        public ActionResult GetAllEmployeeRecords()
        {
            using (var context = new EmployeeContext())
            {
                empList = context
                .Employees
                .ToList();
            }          
            return PartialView("_EmployeeList", empList);
        }


        //Insert Employee Record
        public ActionResult Insert()
        {
            return View();
        }
        [HttpPost]
        public ActionResult Insert(Employee employee)
        {
            EmployeeHub.NotifyCurrentEmployeeInformationToAllClients("Validating Request");

            HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => ProcessingEmployee(employee));          

            return RedirectToAction("Index");
        }

        private ActionResult ProcessingEmployee(Employee employee)
        {
            Task.Delay(300);
            EmployeeHub.NotifyCurrentEmployeeInformationToAllClients("Processing Request");

            HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => ProcessEmployee(employee));

            return RedirectToAction("Index");
        }

        private void ProcessEmployee(Employee employee)
        {
            Task.Delay(200);
            if (ModelState.IsValid)
            {
                //Insert into Employee table 
                using (var context = new EmployeeContext())
                {
                    context.Employees.Add(employee);
                    context.SaveChanges();
                }
                //Once the record is inserted , then notify all the subscribers (Clients)
                EmployeeHub.NotifyCurrentEmployeeInformationToAllClients("Preparing Output");
            }
        }        
    }
}

Let us understand what changes we have made. For that reason let's look into the below piece of code

[HttpPost]
public ActionResult Insert(Employee employee)
{
    EmployeeHub.NotifyCurrentEmployeeInformationToAllClients("Validating Request");

    HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => ProcessingEmployee(employee));          

    return RedirectToAction("Index");
}

As soon as the Server get's the clients request it sends a message to the Hub.

EmployeeHub.NotifyCurrentEmployeeInformationToAllClients("Validating Request")

And by using the HostingEnvironment.QueueBackgroundWorkItem Method, the server schedules the next task that runs in the background independent of any request. So ultimately, we are queuing work on a background thread. The HostingEnvironment.QueueBackgroundWorkItem is available since .NET 4.5.2. Our current environment is .NET 4.6.1. If anybody want to perform the experiment on a previous version, they can use BackgroundTaskManager.

Changes in EmployeeHub.cs file

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;

namespace WebApplication1
{
    public class EmployeeHub : Hub
    { 
        public static void NotifyCurrentEmployeeInformationToAllClients(string message)
        {
            IHubContext context = GlobalHost.ConnectionManager.GetHubContext<EmployeeHub>();

            // the update client method will update the connected client about any recent changes in the server data
            context.Clients.All.updatedClients(message);
        }
    }
}

The above method is very similar to the Hub method of the previous article with the exception that, it can pass message.

Changes in Index.cshtml file

@model IList<WebApplication1.Models.Employee>
@{
    ViewBag.Title = "Index";
}
<link href="~/Content/bootstrap/css/bootstrap.min.css" rel="stylesheet" />

<div>    
        <h1 style="color: green">Send periodic response from the server using SignalR</h1>
         
        <table border="1">            
            <tr>
                <td>
                    <h1 style="color:blueviolet">Add/Update/Delete Employee</h1>
                    <table border="1">
                        <tr>
                            <td>Employee Id</td>
                            <td><input id="txtEmployeeId" type="text" /></td>
                        </tr>
                        <tr>
                            <td>Employee Name</td>
                            <td><input id="txtEmployeeName" type="text" /></td>
                        </tr>
                        <tr>
                            <td>Email Address</td>
                            <td><input id="txtEmail" type="text" /></td>
                        </tr>
                        <tr>
                            <td>Mobile Number</td>
                            <td><input id="txtMobile" type="text" /></td>
                        </tr>
                    </table> 
                    <table border="1">
                        <tr>
                            <td><button id="btnPostEmployee" onclick="InsertEmployee()">Add New Employee</button></td>                           
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
    <br /><br />       
    <div id="dataTable"></div>
    </div>

@section JavaScript{
    <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
    <script src="/signalr/hubs"></script>
    <script type="text/javascript"> 

        $(function () {
            // Create a proxy to signalr hub on web server. It reference the hub.
            var notificationFromHub = $.connection.employeeHub;

            // Connect to signalr hub
            $.connection.hub.start().done(function () {
                FetchEmployees();
            });

            // Notify to client with the recent updates
            notificationFromHub.client.updatedClients = function (serverResponse) {
                alert(serverResponse);
                FetchEmployees();
            };
        });

        function FetchEmployees() {
            var model = $('#dataTable');            
                $.ajax({
                    url: '/home/GetAllEmployeeRecords',                   
                    contentType: 'application/html ; charset:utf-8',
                    type: 'GET',
                    dataType: 'html'
                })
                    .success(function (result)
                    {
                       model.empty().append(result);
                      
                    })
        }

        // Insert Employee Record
        function InsertEmployee()
        {
                var employee = {
                    EmployeeID: $('#txtEmployeeId').val(),
                    EmployeeName: $('#txtEmployeeName').val(),
                    EmailAdress: $('#txtEmail').val(),
                    MobileNumber: $('#txtMobile').val(),                      
                };
           
         
                    $.ajax({
                    url: '/home/Insert',
                    type: 'POST',
                    data: JSON.stringify(employee),
                    contentType: "application/json;charset=utf-8",
                    success: function (data) {                       
                        alert(data);                      
                    },
                    error: function () {
                        alert('Employee not Added');
                    }
                });
        }
        
    </script>
}

In this file, we have removed the Update and Delete buttons and the corresponding Ajax functions.

Now let us run the application

Initial page load

As soon as the user presses the button Add New Employee

Click on "OK" button, and the server will response back

Click on "OK" button, and the server will response back

Click on "OK" button, and the final result will be displayed

Conclusion

Hope this will be an interesting topic of sending periodic update from the server to the client without using the old Long Pooling mechanism using timer. SignalR is really interesting at times and it is one such use case. Hope we all enjoyed this experiment. Thanks for reading. Zipped file attached.

Recommendation
Read Load Testing SignalR using CRANK - Step by Step after this article.
Page copy protected against web site content infringement by Copyscape

About the Author

Rajnilari2015
Full Name: Niladri Biswas (RNA Team)
Member Level: Platinum
Member Status: Member,MVP
Member Since: 3/17/2015 2:41:06 AM
Country: India
-- Thanks & Regards, RNA Team


Login to vote for this post.

Comments or Responses

Login to post response

Comment using Facebook(Author doesn't get notification)