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:

  1. In the first step we will create a sample app to perform CRUD operations.
  2. 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.

  1. CREATE TABLE [dbo].[Customers](
  2. [Id] [bigint] IDENTITY(1,1) NOT NULL,
  3. [CustName] [varchar](100) NULL,
  4. [CustEmail] [varchar](150) NULL,
  5. CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED ([Id] ASC)
  6. WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, _ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]
  7. GO
  8. SET ANSI_PADDING OFF
  9. GO
Stored Procedures
  1. USE [CRUD_Sample]
  2. GO
  3. /****** Object: StoredProcedure [dbo].[Delete_Customer] Script Date: 12/27/2015 1:44:05 PM ******/
  4. SET ANSI_NULLS ON
  5. GO
  6. SET QUOTED_IDENTIFIER ON
  7. GO
  8. -- =============================================
  9. -- Author: <Author,,Name>
  10. -- Create date: <Create Date,,>
  11. -- Description: <Description,,>
  12. -- =============================================
  13. CREATE PROCEDURE [dbo].[Delete_Customer]
  14. -- Add the parameters for the stored procedure here
  15. @Id Bigint
  16. AS
  17. BEGIN
  18. -- SET NOCOUNT ON added to prevent extra result sets from
  19. -- interfering with SELECT statements.
  20. SET NOCOUNT ON;
  21. -- Insert statements for procedure here
  22. DELETE FROM [dbo].[Customers] WHERE [Id] = @Id
  23. SELECT 1
  24. END
  25. GO
  26. /****** Object: StoredProcedure [dbo].[Get_Customer] Script Date: 12/27/2015 1:44:05 PM ******/
  27. SET ANSI_NULLS ON
  28. GO
  29. SET QUOTED_IDENTIFIER ON
  30. GO
  31. -- =============================================
  32. -- Author: <Author,,Name>
  33. -- Create date: <Create Date,,>
  34. -- Description: <Description,,>
  35. -- =============================================
  36. CREATE PROCEDURE [dbo].[Get_Customer]
  37. -- Add the parameters for the stored procedure here
  38. @Count INT
  39. AS
  40. BEGIN
  41. -- SET NOCOUNT ON added to prevent extra result sets from
  42. -- interfering with SELECT statements.
  43. SET NOCOUNT ON;
  44. -- Insert statements for procedure here
  45. SELECT top(@Count)* FROM [dbo].[Customers]
  46. END
  47. GO
  48. /****** Object: StoredProcedure [dbo].[Get_CustomerbyID] Script Date: 12/27/2015 1:44:05 PM ******/
  49. SET ANSI_NULLS ON
  50. GO
  51. SET QUOTED_IDENTIFIER ON
  52. GO
  53. -- =============================================
  54. -- Author: <Author,,Name>
  55. -- Create date: <Create Date,,>
  56. -- Description: <Description,,>
  57. -- =============================================
  58. CREATE PROCEDURE [dbo].[Get_CustomerbyID]
  59. -- Add the parameters for the stored procedure here
  60. @Id BIGINT
  61. AS
  62. BEGIN
  63. -- SET NOCOUNT ON added to prevent extra result sets from
  64. -- interfering with SELECT statements.
  65. SET NOCOUNT ON;
  66. -- Insert statements for procedure here
  67. SELECT * FROM [dbo].[Customers]
  68. WHERE Id=@Id
  69. END
  70. GO
  71. /****** Object: StoredProcedure [dbo].[Set_Customer] Script Date: 12/27/2015 1:44:05 PM ******/
  72. SET ANSI_NULLS ON
  73. GO
  74. SET QUOTED_IDENTIFIER ON
  75. GO
  76. -- =============================================
  77. -- Author: <Author,,Name>
  78. -- Create date: <Create Date,,>
  79. -- Description: <Description,,>
  80. -- =============================================
  81. CREATE PROCEDURE [dbo].[Set_Customer]
  82. -- Add the parameters for the stored procedure here
  83. @CustName Nvarchar(100)
  84. ,@CustEmail Nvarchar(150)
  85. AS
  86. BEGIN
  87. -- SET NOCOUNT ON added to prevent extra result sets from
  88. -- interfering with SELECT statements.
  89. SET NOCOUNT ON;
  90. -- Insert statements for procedure here
  91. INSERT INTO [dbo].[Customers]([CustName],[CustEmail])
  92. VALUES(@CustName,@CustEmail)
  93. SELECT 1
  94. END
  95. GO
  96. /****** Object: StoredProcedure [dbo].[Update_Customer] Script Date: 12/27/2015 1:44:05 PM ******/
  97. SET ANSI_NULLS ON
  98. GO
  99. SET QUOTED_IDENTIFIER ON
  100. GO
  101. -- =============================================
  102. -- Author: <Author,,Name>
  103. -- Create date: <Create Date,,>
  104. -- Description: <Description,,>
  105. -- =============================================
  106. CREATE PROCEDURE [dbo].[Update_Customer]
  107. -- Add the parameters for the stored procedure here
  108. @Id Bigint
  109. ,@CustNameNvarchar(100)
  110. ,@CustEmailNvarchar(150)
  111. AS
  112. BEGIN
  113. -- SET NOCOUNT ON added to prevent extra result sets from
  114. -- interfering with SELECT statements.
  115. SET NOCOUNT ON;
  116. -- Insert statements for procedure here
  117. UPDATE [dbo].[Customers] SET[CustName] = @CustName,[CustEmail]= @CustEmail
  118. WHERE [Id] = @Id
  119. SELECT 1
  120. END
  121. GO
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:

Getting Started withMVC

Step 2:

Web application

Step 3:

select template

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.
  1. namespace WebApplication1.Repository
  2. {
  3. interfaceIRepository < T > : IDisposablewhereT: class
  4. {
  5. IEnumerable < T > ExecuteQuery(stringspQuery, object[] parameters);
  6. TExecuteQuerySingle(stringspQuery, object[] parameters);
  7. intExecuteCommand(stringspQuery, object[] parameters);
  8. }
  9. }
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.
  1. namespace WebApplication1.Repository
  2. {
  3. public class GenericRepository < T > : IRepository < T > whereT: class
  4. {
  5. Customer_Entities context = null;
  6. privateDbSet < T > entities = null;
  7. public GenericRepository(Customer_Entities context)
  8. {
  9. this.context = context;
  10. entities = context.Set < T > ();
  11. }
  12. ///<summary>
  13. /// Get Data From Database
  14. ///<para>Use it when to retive data through a stored procedure</para>
  15. ///</summary>
  16. public IEnumerable < T > ExecuteQuery(stringspQuery, object[] parameters)
  17. {
  18. using(context = newCustomer_Entities())
  19. {
  20. returncontext.Database.SqlQuery < T > (spQuery, parameters).ToList();
  21. }
  22. }
  23. ///<summary>
  24. /// Get Single Data From Database
  25. ///<para>Use it when to retive single data through a stored procedure</para>
  26. ///</summary>
  27. public TExecuteQuerySingle(stringspQuery, object[] parameters)
  28. {
  29. using(context = newCustomer_Entities())
  30. {
  31. returncontext.Database.SqlQuery < T > (spQuery, parameters).FirstOrDefault();
  32. }
  33. }
  34. ///<summary>
  35. /// Insert/Update/Delete Data To Database
  36. ///<para>Use it when to Insert/Update/Delete data through a stored procedure</para>
  37. ///</summary>
  38. public intExecuteCommand(stringspQuery, object[] parameters)
  39. {
  40. int result = 0;
  41. try
  42. {
  43. using(context = newCustomer_Entities())
  44. {
  45. result = context.Database.SqlQuery < int > (spQuery, parameters).FirstOrDefault();
  46. }
  47. }
  48. catch
  49. {}
  50. return result;
  51. }
  52. private bool disposed = false;
  53. protected virtualvoid Dispose(bool disposing)
  54. {
  55. if (!this.disposed)
  56. {
  57. if (disposing)
  58. {
  59. context.Dispose();
  60. }
  61. }
  62. this.disposed = true;
  63. }
  64. public void Dispose()
  65. {
  66. Dispose(true);
  67. GC.SuppressFinalize(this);
  68. }
  69. }
  70. }
Use of middle-tire
  1. namespace WebApplication1.Services
  2. {
  3. public partial class CustomerService
  4. {
  5. privateGenericRepository < Customer > CustRepository;
  6. //CustomerRepositoryCustRepository;
  7. public CustomerService()
  8. {
  9. this.CustRepository = newGenericRepository < Customer > (newCustomer_Entities());
  10. }
  11. public IEnumerable < Customer > GetAll(object[] parameters)
  12. {
  13. stringspQuery = "[Get_Customer] {0}";
  14. returnCustRepository.ExecuteQuery(spQuery, parameters);
  15. }
  16. public CustomerGetbyID(object[] parameters)
  17. {
  18. stringspQuery = "[Get_CustomerbyID] {0}";
  19. returnCustRepository.ExecuteQuerySingle(spQuery, parameters);
  20. }
  21. public int Insert(object[] parameters)
  22. {
  23. stringspQuery = "[Set_Customer] {0}, {1}";
  24. returnCustRepository.ExecuteCommand(spQuery, parameters);
  25. }
  26. public int Update(object[] parameters)
  27. {
  28. stringspQuery = "[Update_Customer] {0}, {1}, {2}";
  29. returnCustRepository.ExecuteCommand(spQuery, parameters);
  30. }
  31. public int Delete(object[] parameters)
  32. {
  33. stringspQuery = "[Delete_Customer] {0}";
  34. returnCustRepository.ExecuteCommand(spQuery, parameters);
  35. }
  36. }
  37. }
Use of Generic Repository in MVC-Application:
  1. namespace WebApplication1.Controllers
  2. {
  3. public class HomeController: Controller
  4. {
  5. private CustomerServiceobjCust;
  6. //CustomerRepositoryCustRepository;
  7. public HomeController()
  8. {
  9. this.objCust = newCustomerService();
  10. }
  11. // GET: Home
  12. public ActionResult Index()
  13. {
  14. int Count = 10;
  15. object[] parameters = {
  16. Count
  17. };
  18. var test = objCust.GetAll(parameters);
  19. return View(test);
  20. }
  21. public ActionResult Insert()
  22. {
  23. return View();
  24. }
  25. [HttpPost]
  26. public ActionResult Insert(Customer model)
  27. {
  28. if (ModelState.IsValid)
  29. {
  30. object[] parameters = {
  31. model.CustName,
  32. model.CustEmail
  33. };
  34. objCust.Insert(parameters);
  35. }
  36. return RedirectToAction("Index");
  37. }
  38. public ActionResult Delete(int id)
  39. {
  40. object[] parameters = {
  41. id
  42. };
  43. this.objCust.Delete(parameters);
  44. return RedirectToAction("Index");
  45. }
  46. public ActionResult Update(int id)
  47. {
  48. object[] parameters = {
  49. id
  50. };
  51. return View(this.objCust.GetbyID(parameters));
  52. }
  53. [HttpPost]
  54. public ActionResult Update(Customer model)
  55. {
  56. object[] parameters = {
  57. model.Id,
  58. model.CustName,
  59. model.CustEmail
  60. };
  61. objCust.Update(parameters);
  62. return RedirectToAction("Index");
  63. }
  64. protected override void Dispose(bool disposing)
  65. {
  66. base.Dispose(disposing);
  67. }
  68. }
  69. }
Use of views in MVC-Application

Index
  1. @model IList
  2. <WebApplication1.Models.Customer>
  3. @{
  4. ViewBag.Title = "Index";
  5. }
  6. <linkhref="~/Content/bootstrap/css/bootstrap.min.css"rel="stylesheet"/>
  7. <divclass="clearfix">
  8. </div>
  9. <divclass="clearfix">
  10. </div>
  11. <divclass="container">
  12. <divclass="table-responsive">
  13. @Html.ActionLink("New Customer", "Insert", "Home")
  14. <tableclass="table table-bordered table-striped">
  15. <thead>
  16. <tr>
  17. <th>ID</th>
  18. <th>Name</th>
  19. <th>Email ID</th>
  20. <th>Delete</th>
  21. <th>Update</th>
  22. </tr>
  23. </thead>
  24. <tbody>
  25. @if (Model != null)
  26. {
  27. foreach (var item in Model)
  28. {
  29. <tr>
  30. <td>@item.Id</td>
  31. <td>@item.CustName</td>
  32. <td>@item.CustEmail</td>
  33. <td>@Html.ActionLink("Delete", "Delete", "Home", new { id = @item.Id }, null)</td>
  34. <td>@Html.ActionLink("Update", "Update", "Home", new { id = @item.Id }, null)</td>
  35. </tr>
  36. }
  37. }
  38. </tbody>
  39. </table>
  40. </div>
  41. <divclass="clearfix">
  42. </div>
  43. </div>
Insert
  1. @model WebApplication1.Models.Customer
  2. @{
  3. ViewBag.Title = "Insert";
  4. }
  5. <link href="~/Content/bootstrap/css/bootstrap.min.css"rel="stylesheet"/>
  6. <div class="clearfix">
  7. </div>
  8. <div class="clearfix">
  9. </div>
  10. <div class="container">
  11. <div class="table-responsive col-md-6 col-md-offset-3">
  12. <table class="table table-bordered table-striped">
  13. <tbody>
  14. @using (Html.BeginForm("Insert", "Home", FormMethod.Post))
  15. {
  16. @*
  17. <tr>
  18. <td class="col-md-4">ID</td>
  19. <td class="col-md-8">@Html.TextBoxFor(m =>m.Id)</td>
  20. </tr>*@
  21. <tr>
  22. <td class="col-md-4">Name
  23. </td>
  24. <td class="col-md-8">@Html.TextBoxFor(m =>m.CustName)
  25. </td>
  26. </tr>
  27. <tr>
  28. <td class="col-md-4">Email ID
  29. </td>
  30. <td class="col-md-8">@Html.TextBoxFor(m =>m.CustEmail)
  31. </td>
  32. </tr>
  33. <tr>
  34. <td class="text-right"colspan="2">
  35. <input type="submit"value="Save"class="btnbtn-primary"/>
  36. </td>
  37. </tr>
  38. }
  39. </tbody>
  40. </table>
  41. </div>
  42. <div class="clearfix">
  43. </div>
  44. @Html.ActionLink("Home", "Index", "Home")
  45. </div>
Update
  1. @model WebApplication1.Models.Customer
  2. @{
  3. ViewBag.Title = "Update";
  4. }
  5. <link href="~/Content/bootstrap/css/bootstrap.min.css"rel="stylesheet"/>
  6. <div class="clearfix">
  7. </div>
  8. <div class="clearfix">
  9. </div>
  10. <div class="container">
  11. <div class="table-responsive">
  12. <table class="table table-bordered table-striped">
  13. <thead>
  14. <tr>
  15. <th>Name</th>
  16. <th>Email ID</th>
  17. <th>Update</th>
  18. </tr>
  19. </thead>
  20. <tbody>
  21. <tr>
  22. @using (Html.BeginForm("Update", "Home", FormMethod.Post))
  23. {
  24. <td>@Html.TextBoxFor(m =>m.CustName)</td>
  25. <td>@Html.TextBoxFor(m =>m.CustEmail)</td>
  26. <td>
  27. <inputtype="submit"value="Update"class="btnbtn-primary"/>
  28. </td>
  29. }
  30. </tr>
  31. </tbody>
  32. </table>
  33. </div>
  34. </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

Install-Package

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
  1. using System;
  2. using System.Threading.Tasks;
  3. using Microsoft.Owin;
  4. using Owin;
  5. [assembly: OwinStartup(typeof (WebAppSignalR.Startup))]
  6. namespace WebAppSignalR
  7. {
  8. public class Startup
  9. {
  10. public void Configuration(IAppBuilder app)
  11. {
  12. app.MapSignalR();
  13. }
  14. }
  15. }
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

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using Microsoft.AspNet.SignalR;
  6. using Microsoft.AspNet.SignalR.Hubs;
  7. namespace WebApplication1.Hubs
  8. {
  9. public class CustomerHub: Hub
  10. {
  11. [HubMethodName("broadcastData")]
  12. public static void BroadcastData()
  13. {
  14. IHubContext context = GlobalHost.ConnectionManager.GetHubContext < CustomerHub > ();
  15. context.Clients.All.updatedData();
  16. }
  17. }
  18. }
Code Explanation
  1. IHubContext context = GlobalHost.ConnectionManager.GetHubContext<CustomerHub>();
It gets the CustomerHub context:
  1. 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

  1. @model IList < WebApplication1.Models.Customer > @
  2. {
  3. ViewBag.Title = "Index";
  4. } < linkhref = "~/Content/bootstrap/css/bootstrap.min.css"
  5. 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>
  6. @section JavaScript
  7. { < scriptsrc = "~/Scripts/jquery.signalR-2.2.0.min.js" > < /script> < scriptsrc = "/signalr/hubs" > < /script> < scripttype = "text/javascript" > $(function ()
  8. {
  9. // Reference the hub.
  10. var hubNotif = $.connection.customerHub;
  11. // Start the connection.
  12. $.connection.hub.start().done(function ()
  13. {
  14. getAll();
  15. });
  16. // Notify while anyChanges.
  17. hubNotif.client.updatedData = function ()
  18. {
  19. getAll();
  20. };
  21. });
  22. function getAll()
  23. {
  24. var model = $('#dataTable');
  25. $.ajax(
  26. {
  27. url: '/home/GetAllData',
  28. contentType: 'application/html ; charset:utf-8',
  29. type: 'GET',
  30. dataType: 'html'
  31. }).success(function (result)
  32. {
  33. model.empty().append(result);
  34. }).error(function (e)
  35. {
  36. alert(e);
  37. });
  38. } < /script>
  39. }
Partial View
  1. <table class="table table-bordered table-striped">
  2. <thead>
  3. <tr>
  4. <th>ID</th>
  5. <th>Name</th>
  6. <th>Email ID</th>
  7. <th>Delete</th>
  8. <th>Update</th>
  9. </tr>
  10. </thead>
  11. <tbody> @if (Model != null) { foreach (var item in Model) {
  12. <tr>
  13. <td>@item.Id</td>
  14. <td>@item.CustName</td>
  15. <td>@item.CustEmail</td>
  16. <td>@Html.ActionLink("Delete", "Delete", "Home", new { id = @item.Id }, null)</td>
  17. <td>@Html.ActionLink("Update", "Update", "Home", new { id = @item.Id }, null)</td>
  18. </tr> } } </tbody>
  19. </table>
Let’s Modify our Existing Controller

Home Controller:

In our home controller we will add a method named GetAllData(). Here's the method.
  1. [HttpGet]
  2. public ActionResult GetAllData()
  3. {
  4. int Count = 10;
  5. object[] parameters = {
  6. Count
  7. };
  8. var test = objCust.GetAll(parameters);
  9. return PartialView("_DataList", test);
  10. }
Here we are returning a partial view with returned data list, and just returning empty.
  1. // GET: Home
  2. public ActionResult Index()
  3. {
  4. return View();
  5. }
Home Controller
  1. public class HomeController: Controller
  2. {
  3. private CustomerService objCust;
  4. //CustomerRepositoryCustRepository;
  5. public HomeController()
  6. {
  7. this.objCust = newCustomerService();
  8. }
  9. // GET: Home
  10. public ActionResult Index()
  11. {
  12. return View();
  13. }
  14. [HttpGet]
  15. public ActionResult GetAllData()
  16. {
  17. int Count = 10;
  18. object[] parameters = {
  19. Count
  20. };
  21. var test = objCust.GetAll(parameters);
  22. return PartialView("_DataList", test);
  23. }
  24. public ActionResult Insert()
  25. {
  26. return View();
  27. }
  28. [HttpPost]
  29. public ActionResult Insert(Customer model)
  30. {
  31. if (ModelState.IsValid)
  32. {
  33. object[] parameters = {
  34. model.CustName,
  35. model.CustEmail
  36. };
  37. objCust.Insert(parameters);
  38. }
  39. //Notify to all
  40. CustomerHub.BroadcastData();
  41. return RedirectToAction("Index");
  42. }
  43. public ActionResult Delete(int id)
  44. {
  45. object[] parameters = {
  46. id
  47. };
  48. this.objCust.Delete(parameters);
  49. //Notify to all
  50. CustomerHub.BroadcastData();
  51. return RedirectToAction("Index");
  52. }
  53. public ActionResult Update(int id)
  54. {
  55. object[] parameters = {
  56. id
  57. };
  58. return View(this.objCust.GetbyID(parameters));
  59. }
  60. [HttpPost]
  61. public ActionResult Update(Customer model)
  62. {
  63. object[] parameters = {
  64. model.Id,
  65. model.CustName,
  66. model.CustEmail
  67. };
  68. objCust.Update(parameters);
  69. //Notify to all
  70. CustomerHub.BroadcastData();
  71. returnRedirectToAction("Index");
  72. }
  73. protected override void Dispose(bool disposing)
  74. {
  75. base.Dispose(disposing);
  76. }
  77. }
Output

run

I hope this will help someone.