通过 SignalR 类库,实现 ASP.NET MVC 的实时通信
在本文中,您将学到在现有 ASP.NET MVC 框架的 CRUD 项目中,如何使用 SignalR 类库,显示来自数据库的实时更新。在这一主题中,我们将重点放在在现有 ASP.NET MVC 框架的 CRUD 项目中,如何使用 SignalR 类库,显示来自数据库的实时更新。 本文系国内 ITOM 管理平台 OneAPM 工程师编译整理。
本主题有以下两个步骤:
-
我们将创建一个示例应用程序来执行 CRUD 操作。
-
我们将使用 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:
Step 2:
Step 3:
点击 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);
}
}
输出
希望能够帮助到您。
OneAPM 助您轻松锁定 .NET 应用性能瓶颈,通过强大的 Trace 记录逐层分析,直至锁定行级问题代码。以用户角度展示系统响应速度,以地域和浏览器维度统计用户使用情况。想阅读更多技术文章,请访问 OneAPM 官方博客。
本文转自 OneAPM 官方博客
原文地址: http://www.c-sharpcorner.com/UploadFile/302f8f/Asp-Net-mvc-real-time-app-with-signalr/