通过 SignalR 类库,实现 ASP.NET MVC 的实时通信

时间:2022-06-26 04:22:43

在本文中,您将学到在现有 ASP.NET MVC 框架的 CRUD 项目中,如何使用 SignalR 类库,显示来自数据库的实时更新。在这一主题中,我们将重点放在在现有 ASP.NET MVC 框架的 CRUD 项目中,如何使用 SignalR 类库,显示来自数据库的实时更新。 本文系国内 ITOM 管理平台 OneAPM 工程师编译整理。

本主题有以下两个步骤:

  1. 我们将创建一个示例应用程序来执行 CRUD 操作。

  2. 我们将使用 SignalR 类库让应用实时。

那些不熟悉 SignalR 的,请访问我以前有关 SignalR 概述 的文章。

第一步:

我们需要创建一个名为 CRUD_Sample 的数据库。在示例数据库中创建一个名为 Customer 的表。

CREATE TABLE [dbo].[Customer](
[Id] [bigint] IDENTITY(1,1)NOTNULL,
[CustName] [varchar](100)NULL,
[CustEmail] [varchar](150)NULL
)

存储过程

USE [CRUD_Sample]
GO /****** Object: StoredProcedure [dbo].[Delete_Customer] Script Date: 12/27/2015 1:44:05 PM ******/
SETANSI_NULLSON
GO SETQUOTED_IDENTIFIERON
GO -- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[Delete_Customer]
-- Add the parameters for the stored procedure here
@Id Bigint
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SETNOCOUNTON; -- Insert statements for procedure here
DELETE FROM [dbo].[Customers] WHERE [Id] = @Id
RETURN 1
END GO /****** Object: StoredProcedure [dbo].[Get_Customer] Script Date: 12/27/2015 1:44:05 PM ******/
SETANSI_NULLSON
GO SETQUOTED_IDENTIFIERON
GO -- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[Get_Customer]
-- Add the parameters for the stored procedure here
@Count INT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SETNOCOUNTON; -- Insert statements for procedure here
SELECT top(@Count)*FROM [dbo].[Customers]
END GO /****** Object: StoredProcedure [dbo].[Get_CustomerbyID] Script Date: 12/27/2015 1:44:05 PM ******/
SETANSI_NULLSON
GO SETQUOTED_IDENTIFIERON
GO -- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[Get_CustomerbyID]
-- Add the parameters for the stored procedure here
@Id BIGINT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SETNOCOUNTON; -- Insert statements for procedure here
SELECT*FROM [dbo].[Customers]
WHERE Id=@Id
END GO /****** Object: StoredProcedure [dbo].[Set_Customer] Script Date: 12/27/2015 1:44:05 PM ******/
SETANSI_NULLSON
GO SETQUOTED_IDENTIFIERON
GO -- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[Set_Customer]
-- Add the parameters for the stored procedure here
@CustNameNvarchar(100)
,@CustEmailNvarchar(150)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SETNOCOUNTON; -- Insert statements for procedure here
INSERT INTO [dbo].[Customers]([CustName],[CustEmail])
VALUES(@CustName,@CustEmail)
RETURN 1
END GO /****** Object: StoredProcedure [dbo].[Update_Customer] Script Date: 12/27/2015 1:44:05 PM ******/
SETANSI_NULLSON
GO SETQUOTED_IDENTIFIERON
GO -- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[Update_Customer]
-- Add the parameters for the stored procedure here
@Id Bigint
,@CustNameNvarchar(100)
,@CustEmailNvarchar(150)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SETNOCOUNTON; -- Insert statements for procedure here
UPDATE [dbo].[Customers] SET[CustName] = @CustName,[CustEmail]= @CustEmail
WHERE [Id] = @Id
RETURN 1
END GO

启动 MVC 项目

创建示例应用程序,我们需要 Visual Studio 2012 或更高版本,并且该服务器平台必须支持 .NET 4.5。

步骤 1:

通过 SignalR 类库,实现 ASP.NET MVC 的实时通信

Step 2:

通过 SignalR 类库,实现 ASP.NET MVC 的实时通信

Step 3:

通过 SignalR 类库,实现 ASP.NET MVC 的实时通信

点击 OK,Visual Studio 将会创建一个新的 ASP.NET 工程。

使用通用类库

使用通用功能,我们可以减少代码数量。

namespace WebApplication1.Repository
{
interfaceIRepository < T > : IDisposablewhereT: class
{
IEnumerable < T > ExecuteQuery(stringspQuery, object[] parameters);
TExecuteQuerySingle(stringspQuery, object[] parameters);
intExecuteCommand(stringspQuery, object[] parameters);
}
}

接口 IRepository

显示一个通用类库的 T 型接口,它是 SQL 实体的 LINQ。它提供了一个基本的界面操作,如 Insert, Update, Delete, GetById and GetAll。

IDisposable

IDisposable接口提供了一种机制,释放非托管资源。

where T : class

这是制约泛型参数的一类。点击查看更多

类型参数必须是引用类型;这也适用于任何类,接口,委托或数组类型。

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 > ();
}
///<summary>
/// Get Data From Database
///<para>Use it when to retive data through a stored procedure</para>
///</summary>
public IEnumerable < T > ExecuteQuery(stringspQuery, object[] parameters)
{
using(context = newCustomer_Entities())
{
returncontext.Database.SqlQuery < T > (spQuery, parameters).ToList();
}
}
///<summary>
/// Get Single Data From Database
///<para>Use it when to retive single data through a stored procedure</para>
///</summary>
public TExecuteQuerySingle(stringspQuery, object[] parameters)
{
using(context = newCustomer_Entities())
{
returncontext.Database.SqlQuery < T > (spQuery, parameters).FirstOrDefault();
}
}
///<summary>
/// Insert/Update/Delete Data To Database
///<para>Use it when to Insert/Update/Delete data through a stored procedure</para>
///</summary>
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);
}
}
}

使用 middle-tire 结构

namespace WebApplication1.Services
{
public partial class CustomerService
{
privateGenericRepository < Customer > CustRepository;
//CustomerRepositoryCustRepository;
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);
}
}
}

在 MVC 架构应用程序中使用通用库

namespace WebApplication1.Controllers
{
public class HomeController: Controller
{
private CustomerServiceobjCust;
//CustomerRepositoryCustRepository;
public HomeController()
{
this.objCust = newCustomerService();
}
// GET: Home
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);
}
}
}

在 MVC 架构应用程序中使用视图

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>

步骤2:

启动 SignalR

第一件事是获得 NuGet 参照。

在 NuGet 上获得。

安装 Microsoft.AspNet.SignalR 包

Microsoft.AspNet.SignalR

注册 SignalR 中间件

安装后需要创建 OwinStartup 类。

下面的代码将一段简单中间件向 OWIN 管道,实现接收 Microsoft.Owin.IOwinContext 实例的功能。

当服务器收到一个 HTTP 请求,OWIN 管道调用中间件。中间件设置内容类型的响应和写响应体。

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

创建使用 Hub 类

完成前面的过程之后,创建一个 Hub。一个SignalR Hub 让从服务器到客户端连接,并从客户端到服务器的远程过程调用(RPC)。

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

代码说明

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

获得 CustomerHub context:

context.Clients.All.updatedData();

它请求 SignalR 的客户端部分,并告诉它执行 JavaScript 的 updatedData()方法。

修改现有视图 Let’s Modify our Existing 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 ()
{
// Reference the hub.
var hubNotif = $.connection.customerHub;
// Start the connection.
$.connection.hub.start().done(function ()
{
getAll();
});
// Notify while anyChanges.
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>
}

局部视图

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

修改现有 Controller

主 Controller:

在主 Controller,我们将添加一个名为 GetAllDaTa()的方法。这是方法。

[HttpGet]
public ActionResult GetAllData()
{
int Count = 10;
object[] parameters = {
Count
};
var test = objCust.GetAll(parameters);
return PartialView("_DataList", test);
}

在这里,返回局部视图返回的数据列表,且只返回空。

// GET: Home
public ActionResult Index()
{
return View();
}

主 Controller

public class HomeController: Controller
{
private CustomerService objCust;
//CustomerRepositoryCustRepository;
public HomeController()
{
this.objCust = newCustomerService();
}
// GET: Home
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);
}
//Notify to all
CustomerHub.BroadcastData();
return RedirectToAction("Index");
}
public ActionResult Delete(int id)
{
object[] parameters = {
id
};
this.objCust.Delete(parameters);
//Notify to all
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);
//Notify to all
CustomerHub.BroadcastData();
returnRedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}

输出

通过 SignalR 类库,实现 ASP.NET MVC 的实时通信

希望能够帮助到您。

OneAPM 助您轻松锁定 .NET 应用性能瓶颈,通过强大的 Trace 记录逐层分析,直至锁定行级问题代码。以用户角度展示系统响应速度,以地域和浏览器维度统计用户使用情况。想阅读更多技术文章,请访问 OneAPM 官方博客

本文转自 OneAPM 官方博客

原文地址: http://www.c-sharpcorner.com/UploadFile/302f8f/Asp-Net-mvc-real-time-app-with-signalr/