In this topic we will focus on how to display real time updates from databases with SignalR on existing ASP.NET MVC CRUD project.
The topic has the following two steps:
- In the first step we will create a sample app to perform CRUD operations.
- In the second step we will make the app real-time with SignalR.
Those who are not familiar with SignalR, visit my previous article on Overview of SignalR.
Step 1: At first we need to create a database named CRUD_Sample. In sample db we have to create a table named Customers.
- CREATE TABLE [dbo].[Customers](
- [Id] [bigint] IDENTITY(1,1) NOT NULL,
- [CustName] [varchar](100) NULL,
- [CustEmail] [varchar](150) NULL,
- CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED ([Id] ASC)
- WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, _ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]
-
- GO
-
- SET ANSI_PADDING OFF
- GO
Stored Procedures
Getting Started with MVC Project
To create a sample application, we need to have Visual Studio 2012 or later installed and be able to run the server on a platform that supports .NET 4.5.
Step 1:
Step 2:
Step 3:
Click OK and Visual Studio will create and load a new ASP.NET application project.
Use of Generic Repository
With a generic feature, we can reduce the amount of code we need for common scenarios.
- namespace WebApplication1.Repository
- {
- interfaceIRepository < T > : IDisposablewhereT: class
- {
- IEnumerable < T > ExecuteQuery(stringspQuery, object[] parameters);
- TExecuteQuerySingle(stringspQuery, object[] parameters);
- intExecuteCommand(stringspQuery, object[] parameters);
- }
- }
interfaceIRepository<T>
Show an interface of a generic repository of type T, which is a LINQ to SQL entity. It provides a basic interface with operations like Insert, Update, Delete, GetById and GetAll.
IDisposable
The IDisposable Interface provides a mechanism for releasing unmanaged resources.
whereT : class
This is constraining the generic parameter to a class. Click for
more.
The type of argument must be a reference type; this applies also to any class, interface, delegate, or array type.
- namespace WebApplication1.Repository
- {
- public class GenericRepository < T > : IRepository < T > whereT: class
- {
- Customer_Entities context = null;
- privateDbSet < T > entities = null;
- public GenericRepository(Customer_Entities context)
- {
- this.context = context;
- entities = context.Set < T > ();
- }
-
-
-
-
- public IEnumerable < T > ExecuteQuery(stringspQuery, object[] parameters)
- {
- using(context = newCustomer_Entities())
- {
- returncontext.Database.SqlQuery < T > (spQuery, parameters).ToList();
- }
- }
-
-
-
-
- public TExecuteQuerySingle(stringspQuery, object[] parameters)
- {
- using(context = newCustomer_Entities())
- {
- returncontext.Database.SqlQuery < T > (spQuery, parameters).FirstOrDefault();
- }
- }
-
-
-
-
- public intExecuteCommand(stringspQuery, object[] parameters)
- {
- int result = 0;
- try
- {
- using(context = newCustomer_Entities())
- {
- result = context.Database.SqlQuery < int > (spQuery, parameters).FirstOrDefault();
- }
- }
- catch
- {}
- return result;
- }
- private bool disposed = false;
- protected virtualvoid Dispose(bool disposing)
- {
- if (!this.disposed)
- {
- if (disposing)
- {
- context.Dispose();
- }
- }
- this.disposed = true;
- }
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- }
- }
Use of middle-tire
- namespace WebApplication1.Services
- {
- public partial class CustomerService
- {
- privateGenericRepository < Customer > CustRepository;
-
- public CustomerService()
- {
- this.CustRepository = newGenericRepository < Customer > (newCustomer_Entities());
- }
- public IEnumerable < Customer > GetAll(object[] parameters)
- {
- stringspQuery = "[Get_Customer] {0}";
- returnCustRepository.ExecuteQuery(spQuery, parameters);
- }
- public CustomerGetbyID(object[] parameters)
- {
- stringspQuery = "[Get_CustomerbyID] {0}";
- returnCustRepository.ExecuteQuerySingle(spQuery, parameters);
- }
- public int Insert(object[] parameters)
- {
- stringspQuery = "[Set_Customer] {0}, {1}";
- returnCustRepository.ExecuteCommand(spQuery, parameters);
- }
- public int Update(object[] parameters)
- {
- stringspQuery = "[Update_Customer] {0}, {1}, {2}";
- returnCustRepository.ExecuteCommand(spQuery, parameters);
- }
- public int Delete(object[] parameters)
- {
- stringspQuery = "[Delete_Customer] {0}";
- returnCustRepository.ExecuteCommand(spQuery, parameters);
- }
- }
- }
Use of Generic Repository in MVC-Application:
- namespace WebApplication1.Controllers
- {
- public class HomeController: Controller
- {
- private CustomerServiceobjCust;
-
- public HomeController()
- {
- this.objCust = newCustomerService();
- }
-
- public ActionResult Index()
- {
- int Count = 10;
- object[] parameters = {
- Count
- };
- var test = objCust.GetAll(parameters);
- return View(test);
- }
- public ActionResult Insert()
- {
- return View();
- }
- [HttpPost]
- public ActionResult Insert(Customer model)
- {
- if (ModelState.IsValid)
- {
- object[] parameters = {
- model.CustName,
- model.CustEmail
- };
- objCust.Insert(parameters);
- }
- return RedirectToAction("Index");
- }
- public ActionResult Delete(int id)
- {
- object[] parameters = {
- id
- };
- this.objCust.Delete(parameters);
- return RedirectToAction("Index");
- }
- public ActionResult Update(int id)
- {
- object[] parameters = {
- id
- };
- return View(this.objCust.GetbyID(parameters));
- }
- [HttpPost]
- public ActionResult Update(Customer model)
- {
- object[] parameters = {
- model.Id,
- model.CustName,
- model.CustEmail
- };
- objCust.Update(parameters);
- return RedirectToAction("Index");
- }
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- }
- }
- }
Use of views in MVC-Application
Index
- @model IList
- <WebApplication1.Models.Customer>
- @{
- ViewBag.Title = "Index";
- }
-
- <linkhref="~/Content/bootstrap/css/bootstrap.min.css"rel="stylesheet"/>
- <divclass="clearfix">
- </div>
- <divclass="clearfix">
- </div>
- <divclass="container">
- <divclass="table-responsive">
- @Html.ActionLink("New Customer", "Insert", "Home")
-
- <tableclass="table table-bordered table-striped">
- <thead>
- <tr>
- <th>ID</th>
- <th>Name</th>
- <th>Email ID</th>
- <th>Delete</th>
- <th>Update</th>
- </tr>
- </thead>
- <tbody>
- @if (Model != null)
- {
- foreach (var item in Model)
- {
-
- <tr>
- <td>@item.Id</td>
- <td>@item.CustName</td>
- <td>@item.CustEmail</td>
- <td>@Html.ActionLink("Delete", "Delete", "Home", new { id = @item.Id }, null)</td>
- <td>@Html.ActionLink("Update", "Update", "Home", new { id = @item.Id }, null)</td>
- </tr>
- }
- }
-
- </tbody>
- </table>
- </div>
- <divclass="clearfix">
- </div>
- </div>
Insert
- @model WebApplication1.Models.Customer
- @{
- ViewBag.Title = "Insert";
- }
-
- <link href="~/Content/bootstrap/css/bootstrap.min.css"rel="stylesheet"/>
- <div class="clearfix">
- </div>
- <div class="clearfix">
- </div>
- <div class="container">
- <div class="table-responsive col-md-6 col-md-offset-3">
- <table class="table table-bordered table-striped">
- <tbody>
- @using (Html.BeginForm("Insert", "Home", FormMethod.Post))
- {
- @*
- <tr>
- <td class="col-md-4">ID</td>
- <td class="col-md-8">@Html.TextBoxFor(m =>m.Id)</td>
- </tr>*@
-
- <tr>
- <td class="col-md-4">Name
- </td>
- <td class="col-md-8">@Html.TextBoxFor(m =>m.CustName)
- </td>
- </tr>
- <tr>
- <td class="col-md-4">Email ID
- </td>
- <td class="col-md-8">@Html.TextBoxFor(m =>m.CustEmail)
- </td>
- </tr>
- <tr>
- <td class="text-right"colspan="2">
- <input type="submit"value="Save"class="btnbtn-primary"/>
- </td>
- </tr>
- }
-
- </tbody>
- </table>
- </div>
- <div class="clearfix">
- </div>
- @Html.ActionLink("Home", "Index", "Home")
-
- </div>
Update
- @model WebApplication1.Models.Customer
- @{
- ViewBag.Title = "Update";
- }
-
-
- <link href="~/Content/bootstrap/css/bootstrap.min.css"rel="stylesheet"/>
- <div class="clearfix">
- </div>
- <div class="clearfix">
- </div>
- <div class="container">
- <div class="table-responsive">
- <table class="table table-bordered table-striped">
- <thead>
- <tr>
- <th>Name</th>
- <th>Email ID</th>
- <th>Update</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- @using (Html.BeginForm("Update", "Home", FormMethod.Post))
- {
-
- <td>@Html.TextBoxFor(m =>m.CustName)</td>
- <td>@Html.TextBoxFor(m =>m.CustEmail)</td>
- <td>
- <inputtype="submit"value="Update"class="btnbtn-primary"/>
- </td>
- }
-
- </tr>
- </tbody>
- </table>
- </div>
- </div>
Step 2:
Getting Started with SignalR
The first thing is getting a reference from NuGet.
Get it on NuGet!
Install-Package
Microsoft.AspNet.SignalR
Register SignalR middleware
Once you have installed it let’s create
OwinStartup Class.
The following code adds a simple piece of middleware to the OWIN pipeline, 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.
Startup.cs
- using System;
- using System.Threading.Tasks;
- using Microsoft.Owin;
- using Owin;
- [assembly: OwinStartup(typeof (WebAppSignalR.Startup))]
- namespace WebAppSignalR
- {
- public class Startup
- {
- public void Configuration(IAppBuilder app)
- {
- app.MapSignalR();
- }
- }
- }
Create &Use Hub classes
After finishing the previous process, let’s create a Hub. A SignalR Hub make remote procedure calls (RPCs) from a server to connected clients and from clients to the server.
CustomerHub.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using Microsoft.AspNet.SignalR;
- using Microsoft.AspNet.SignalR.Hubs;
- namespace WebApplication1.Hubs
- {
- public class CustomerHub: Hub
- {
- [HubMethodName("broadcastData")]
- public static void BroadcastData()
- {
- IHubContext context = GlobalHost.ConnectionManager.GetHubContext < CustomerHub > ();
- context.Clients.All.updatedData();
- }
- }
- }
Code Explanation
- IHubContext context = GlobalHost.ConnectionManager.GetHubContext<CustomerHub>();
It gets the CustomerHub context:
- context.Clients.All.updatedData();
It call the client part of SignalR and tell it to execute the JavaScript method updatedData().
Let’s Modify our Existing View
Now we will modify part of Index view as in the following, and we will display data with a partial view.
Index
- @model IList < WebApplication1.Models.Customer > @
- {
- ViewBag.Title = "Index";
- } < linkhref = "~/Content/bootstrap/css/bootstrap.min.css"
- rel = "stylesheet" / > < divclass = "clearfix" > & nbsp; < /div> < divclass = "clearfix" > & nbsp; < /div> < divclass = "container" > < divclass = "table-responsive" > @Html.ActionLink("New Customer", "Insert", "Home") < hr / > < divid = "dataTable" > < /div> < /div> < divclass = "clearfix" > & nbsp; < /div> < /div>
- @section JavaScript
- { < scriptsrc = "~/Scripts/jquery.signalR-2.2.0.min.js" > < /script> < scriptsrc = "/signalr/hubs" > < /script> < scripttype = "text/javascript" > $(function ()
- {
-
- var hubNotif = $.connection.customerHub;
-
- $.connection.hub.start().done(function ()
- {
- getAll();
- });
-
- hubNotif.client.updatedData = function ()
- {
- getAll();
- };
- });
- function getAll()
- {
- var model = $('#dataTable');
- $.ajax(
- {
- url: '/home/GetAllData',
- contentType: 'application/html ; charset:utf-8',
- type: 'GET',
- dataType: 'html'
- }).success(function (result)
- {
- model.empty().append(result);
- }).error(function (e)
- {
- alert(e);
- });
- } < /script>
- }
Partial View
- <table class="table table-bordered table-striped">
- <thead>
- <tr>
- <th>ID</th>
- <th>Name</th>
- <th>Email ID</th>
- <th>Delete</th>
- <th>Update</th>
- </tr>
- </thead>
- <tbody> @if (Model != null) { foreach (var item in Model) {
- <tr>
- <td>@item.Id</td>
- <td>@item.CustName</td>
- <td>@item.CustEmail</td>
- <td>@Html.ActionLink("Delete", "Delete", "Home", new { id = @item.Id }, null)</td>
- <td>@Html.ActionLink("Update", "Update", "Home", new { id = @item.Id }, null)</td>
- </tr> } } </tbody>
- </table>
Let’s Modify our Existing Controller
Home Controller:
In our home controller we will add a method named GetAllData(). Here's the method.
- [HttpGet]
- public ActionResult GetAllData()
- {
- int Count = 10;
- object[] parameters = {
- Count
- };
- var test = objCust.GetAll(parameters);
- return PartialView("_DataList", test);
- }
Here we are returning a partial view with returned data list, and just returning empty.
-
- public ActionResult Index()
- {
- return View();
- }
Home Controller
- public class HomeController: Controller
- {
- private CustomerService objCust;
-
- public HomeController()
- {
- this.objCust = newCustomerService();
- }
-
- public ActionResult Index()
- {
- return View();
- }
- [HttpGet]
- public ActionResult GetAllData()
- {
- int Count = 10;
- object[] parameters = {
- Count
- };
- var test = objCust.GetAll(parameters);
- return PartialView("_DataList", test);
- }
- public ActionResult Insert()
- {
- return View();
- }
- [HttpPost]
- public ActionResult Insert(Customer model)
- {
- if (ModelState.IsValid)
- {
- object[] parameters = {
- model.CustName,
- model.CustEmail
- };
- objCust.Insert(parameters);
- }
-
- CustomerHub.BroadcastData();
- return RedirectToAction("Index");
- }
- public ActionResult Delete(int id)
- {
- object[] parameters = {
- id
- };
- this.objCust.Delete(parameters);
-
- CustomerHub.BroadcastData();
- return RedirectToAction("Index");
- }
- public ActionResult Update(int id)
- {
- object[] parameters = {
- id
- };
- return View(this.objCust.GetbyID(parameters));
- }
- [HttpPost]
- public ActionResult Update(Customer model)
- {
- object[] parameters = {
- model.Id,
- model.CustName,
- model.CustEmail
- };
- objCust.Update(parameters);
-
- CustomerHub.BroadcastData();
- returnRedirectToAction("Index");
- }
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- }
- }
Output
I hope this will help someone.