CRUD using SignalR,MVC and Entity Framework (Code First) - Step by Step

Rajnilari2015
Posted by in SignalR category on for Beginner level | Points: 250 | Views : 31064 red flag
Rating: 3.5 out of 5  
 2 vote(s)

This article particularly focus on a situation where multiple connected clients can perform the real time action for data insertion/updation/deletion and fetching by using MVC4, Entity Framework (Code First Approach) and SignalR.


 Download source code for CRUD using SignalR,MVC and Entity Framework (Code First) - Step by Step

Recommendation
Read CRUD using Code First Approach of Entity Framework (EF) before this article.

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.

Entity Framework (EF) is an Object Relational Mapper (ORM) that helps to work with relational data using domain-specific objects. For accessing the data, we will use the Code First Approach of EF.

ASP.NET MVC is a framework for building scalable, standards-based web applications.

This article particularly focus on a situation where multiple connected clients can perform the real time action for data insertion/updation/deletion and fetching by using MVC4, Entity Framework (Code First Approach) and SignalR.

N.B.~ Please read CRUD using Code First Approach of Entity Framework (EF) if you are new to Entity Framework (Code First Approach).

Straight to Experiment - Step by Step

Step 1 : Create MVC Project and add EF references

Let us fire Visual Studio 2015 and create a Web project (MVC project) first. Once done, then let's issue the following command from the Package Manager Console to install Entity Framework

Install-Package EntityFramework

Step 2 :Employee Model creation inside the Models Folder

Let us create an Employee entity class as under

using System.ComponentModel.DataAnnotations;

namespace WebApplication1.Models
{
    public class Employee
    {
        [Key]
        public int EmployeeID { get; set; }
        public string EmployeeName { get; set; }
        public string EmailAdress { get; set; }
        public string MobileNumber { get; set; }
    }
}

Step 3 :Create the Data Context class

Next create the EmployeeContext class as under

using System.Data.Entity;
using WebApplication1.Models;

namespace WebApplication1
{
    public class EmployeeContext : DbContext
    {
        public EmployeeContext() : base("dbConnectionString")
        {
            Database.SetInitializer<EmployeeContext>(new CreateDatabaseIfNotExists<EmployeeContext>());
        }
        public DbSet<Employee> Employees { get; set; }
    }
}

Step 4 :Add Connection String in Web.config

The next thing is to set up the connection string in web.config

<connectionStrings>
    <add name="dbConnectionString" connectionString="Data Source=yourdatasource;Initial Catalog=EmployeeDB;User ID=youdbuserid;Password=yourdbpassword;pooling=false; MultipleActiveResultSets=True;pooling=false;" providerName="System.Data.SqlClient" />
</connectionStrings>

Step 5 :Install SignalR using Nuget

Next we will install SignalR by issuing the below command from the Package Manager Console

Install-Package Microsoft.AspNet.SignalR

Next we need to create Startup.cs file. The purpose of this file is to add a simple piece of middleware to the OWIN pipeline. For this to happen, let us right click on the WebApp project ->Add -> OWIN Startup class

Provide a class name say Startup

And the below will appear

We find the Startup class is decorated with

[assembly: OwinStartup(typeof(WebApp.Startup))]

This indicates that the class is implemented as a function that receives a Microsoft.Owin.IOwinContext instance. When the server receives an HTTP request, the OWIN pipeline invokes the middleware. The middleware sets the content type for the response and writes the response body.

Now, inside the Configuration function, we need to map SignalR hubs to the app builder pipeline at "/signalr". This can be done using the below way

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(WebApplication1.Startup))]

namespace WebApplication1
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

Next we need to create a Hub. A Hub is a class used for the communication for perstistent connections between client and server (hub). It makes Remote Procedure Calls (RPCs) from server to connected clients and from clients to the server. For this let us create a EmployeeHub.cs file and write the below code

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

namespace WebApplication1
{
    public class EmployeeHub : Hub
    {
        [HubMethodName("NotifyClients")]
        public static void NotifyCurrentEmployeeInformationToAllClients()
        {
            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();
        }
    }
}

Hub is an abstract class that provides methods that communicate with SignalR connections that is connected to Microsoft.AspNet.SignalR.Hub. The

IHubContext context = GlobalHost.ConnectionManager.GetHubContext<EmployeeHub>();

gets the EmployeeHub context. The method updatedClients will the update client method will update the connected client about any recent changes in the server data.

context.Clients.All.updatedClients();

It calls the SignalR client part and informs it to execute the JavaScript method updatedClients(). The importance of Hub lies in sending different kinds of messages and data between client and server.

.

Step 6 :Create the Controller

Next we need to create the Controller as under

using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
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)
        {  
                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();
            return RedirectToAction("Index");
        }

        //Update Employee Record
        public ActionResult Update()
        {
            return View();
        }
        [HttpPost]
        public ActionResult Update(Employee employee)
        {
            using (var context = new EmployeeContext())
            {
                var empRecord = context.Employees.Find(employee.EmployeeID);

                empRecord.EmployeeName = employee.EmployeeName;
                empRecord.EmailAdress = employee.EmailAdress;
                empRecord.MobileNumber = employee.MobileNumber;

                context.Employees.Add(empRecord);

                context.Entry(empRecord).State = EntityState.Modified;
                context.SaveChanges();
            }

            //Once the record is inserted , then notify all the subscribers (Clients)
            EmployeeHub.NotifyCurrentEmployeeInformationToAllClients();
            return RedirectToAction("Index");
        }

        //Delete Employee Record
        [HttpPost]
        public ActionResult Delete(Employee employee)
        {
            using (var context = new EmployeeContext())
            {
                var empRecord = context.Employees.Find(employee.EmployeeID); 
                context.Employees.Remove(empRecord);
                context.SaveChanges();
            }

            //Once the record is inserted , then notify all the subscribers (Clients)
            EmployeeHub.NotifyCurrentEmployeeInformationToAllClients();
            return RedirectToAction("Index");
        }
    }
}

Step 7 :Shaping the View

Finally, it's turn to make the UI design and invoke the Controller API's and notify the clients about the instantaneous updates which SignalR will take care. Let us first code the Index.cshtml file as under

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

<div>    
        <h1 style="color: green">CRUD using SignalR,MVC and Entity Framework</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>
                            <td>
                                <button id="btnPutEmployee" onclick="UpdateEmployee()">Update Employee</button>
                                <button id="btnDeleteEmployee" onclick="DeleteEmployee();return false;">Delete 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 () {
                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('Employee added Successfully');                       
                    },
                    error: function () {
                        alert('Employee not Added');
                    }
                });
        }

        // Update Employee Record
        function UpdateEmployee() {

            var employee = {
                EmployeeID: $('#txtEmployeeId').val(),
                EmployeeName: $('#txtEmployeeName').val(),
                EmailAdress: $('#txtEmail').val(),
                MobileNumber: $('#txtMobile').val()
            };

            $.ajax({
                url: '/home/Update',
                type: 'POST',
                data: JSON.stringify(employee),
                contentType: "application/json;charset=utf-8",
                success: function (data) {
                    alert('Employee updated Successfully');                    
                },
                error: function (e) {                    
                    alert('Employee could not be updated');
                }
            });
        }

        // Delete Employee Record
        function DeleteEmployee() {
            
            var employee = {
                EmployeeID: $('#txtEmployeeId').val()
            };

            $.ajax({
                url: '/home/Delete',
                type: 'POST',
                data: JSON.stringify(employee),
                contentType: "application/json;charset=utf-8",
                success: function (data) {                   
                    alert('Employee deleted Successfully');
                },
                error: function (x, y, z) {
                    alert(x + '\n' + y + '\n' + z);
                }
            });
        }
    </script>
}

The code snippet

$(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 () {
                FetchEmployees();
            };
        });

indicates that, first we are creating a proxy to SignalR Hub on web server. Once done, then we need to establish a connection to the Hub. The server will notify the clients with the recent updates of the records through the updatedClients method.

Next create a partial view _EmployeeList.cshtml that will be use for displaying the Employee Records.

<table class="table table-bordered table-striped">
    <thead>
        <tr>
            <th>EmployeeID</th>
            <th>EmployeeName</th>
            <th>Email ID</th>
            <th>PhoneNumber</th>
        </tr>
    </thead>
    <tbody>
        @if (Model != null)
            {
                foreach (var item in Model)
                {

                <tr>
                    <td>@item.EmployeeID</td>
                    <td>@item.EmployeeName</td>
                    <td>@item.EmailAdress</td>
                    <td>@item.MobileNumber</td>
                </tr>
            }
        }

    </tbody>
</table>

The entire project structure looks as under

Step 8 :Run the Application

Let us open the Application in two different browsers say one in Chrome and the other in FireFox.

The landing page looks as under

Let us Insert an Employee Record from Chrome Browser and see the reflection in FireFox

We can figure out that an instantaneous update has happen without delay in waiting time and no flickering. The same happens for other operations.

N.B.~ If we are changing the Model once the DB tables has been created, then we need to

		1. Enable-Migrations first,
		2. then add-migration
		3. followed by update-database commands from package manager console.
	

Reference

  1. Tutorial: Getting Started with SignalR 2
  2. Introduction to SignalR
  3. CRUD using Code First Approach of Entity Framework (EF)

Conclusion

Hope this will be helpful to understand the "real time"   notification by using SignalR. Thanks for reading. Zipped file attached.

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,Microsoft_MVP,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

Posted by: Bhuvanesh6 on: 7/21/2016 | Points: 25
Nice share, Thanks
Posted by: Bhuvanesh6 on: 7/25/2016 | Points: 25
In some articles they have instructed to use Database to store the connection information like Hub Context information in tables, which is mandate ?
Posted by: Xerxesasimov on: 12/16/2016 | Points: 25
Thanks.
Posted by: Jayakumars on: 9/11/2017 | Points: 25
Hi

After inserted records your code did not bind records can you update them

Login to post response

Comment using Facebook(Author doesn't get notification)