原文:https://blog.csdn.net/mzl87/article/details/104264781
介绍
在这个简单的示例中,我们将看到发生在SQL Server数据库表更改时如何更新HTML页面,而无需重新加载页面或从客户端到服务器进行异步调用,而是从客户端获取此HTML刷新内容。服务器使用Blazor服务器端(.NET CORE 3.0)。
背景
之前,我发表了一篇有关“使用SignalR和SQLTableDependency进行记录更改的SQL Server通知”的文章。
上一篇文章使用了SignalR,以获取实时更改页面内容的通知。尽管功能正常,在我看来,SignalR不是那么直接和容易使用。
在Blazor的帮助下,从服务器到HTML页面的通知得到了极大的简化,从而获得了极好的抽象水平:使用Blazor——实际上——我们的代码只是C#和Razor语法。
使用代码
假设您有一个报告库存清单的页面,并且其中任何一种价格发生变化时,都需要刷新HTML页面。
在SignalR之前,通常有一个使用Ajax 的JavaScript代码来定期(例如,每5秒一次)向服务器执行一个GET请求,以便检索可能的新价格并将其显示在HTML页面中。
如今,借助Blazor及其嵌入式SignalR功能,我们可以扭转这一趋势,并让服务器有责任仅在显示一些新价格时才更新HTML页面。
在下面的例子中,Blazor会负责更新HTML页面,而SqlTableDependency组件会负责在由于insert,update或delete而更改表内容时从SQL Server数据库获取通知:
我们必须使用Visual Studio 2019中的适当模板创建.NET CORE 3.0 Blazor Web应用程序。
然后,我们安装SqlTableDependency NuGet软件包,该软件包将负责获取有关记录表更改的通知:
代码语言:javascript复制PM> Install-Package SqlTableDependency
现在,对于此示例,让我们考虑要监视以下SQL Server表的值:
代码语言:javascript复制CREATE TABLE [dbo].[Stocks](
[Code] [nvarchar](50) NULL,
[Name] [nvarchar](50) NULL,
[Price] [decimal](18, 0) NULL
) ON [PRIMARY]
因此,我们定义了一个C#模型类,该类映射了我们感兴趣的属性:
代码语言:javascript复制namespace BlazorApp1.Models
{
public class Stock
{
public decimal Price { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
}
现在,我们创建一个SqlTableDependency单例实例,将记录表更改包装并转发到Blazor页面。我们开始创建其接口:
代码语言:javascript复制using BlazorApp1.Models;
using System;
using System.Collections.Generic;
namespace BlazorApp1.Service
{
public delegate void StockChangeDelegate(object sender, StockChangeEventArgs args);
public class StockChangeEventArgs : EventArgs
{
public Stock NewValue { get; }
public Stock OldValue { get; }
public StockChangeEventArgs(Stock newValue, Stock oldValue)
{
this.NewValue = newValue;
this.OldValue = oldValue;
}
}
public interface ITableChangeBroadcastService : IDisposable
{
event StockChangeDelegate OnStockChanged;
IList<Stock> GetCurrentValues();
}
}
然后它实现:
代码语言:javascript复制using BlazorApp1.Models;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using TableDependency.SqlClient;
using TableDependency.SqlClient.Base.EventArgs;
namespace BlazorApp1.Service
{
public class TableChangeBroadcastService : ITableChangeBroadcastService
{
private const string TableName = "Stocks";
private SqlTableDependency<Stock> _notifier;
private IConfiguration _configuration;
public event StockChangeDelegate OnStockChanged;
public TableChangeBroadcastService(IConfiguration configuration)
{
_configuration = configuration;
// SqlTableDependency will trigger an event
// for any record change on monitored table
_notifier = new SqlTableDependency<Stock>(
_configuration["ConnectionString"],
TableName);
_notifier.OnChanged = this.TableDependency_Changed;
_notifier.Start();
}
// This method will notify the Blazor component about the stock price change stock
private void TableDependency_Changed(object sender, RecordChangedEventArgs<Stock> e)
{
this. OnStockChanged(this, new StockChangeEventArgs(e.Entity, e.EntityOldValues));
}
// This method is used to populate the HTML view
// when it is rendered for the first time
public IList<Stock> GetCurrentValues()
{
var result = new List<Stock>();
using (var sqlConnection = new SqlConnection(_configuration["ConnectionString"]))
{
sqlConnection.Open();
using (var command = sqlConnection.CreateCommand())
{
command.CommandText = "SELECT * FROM " TableName;
command.CommandType = CommandType.Text;
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
result.Add(new Stock
{
Code = reader.GetString(reader.GetOrdinal("Code")),
Name = reader.GetString(reader.GetOrdinal("Name")),
Price = reader.GetDecimal(reader.GetOrdinal("Price"))
});
}
}
}
}
}
return result;
}
public void Dispose()
{
_notifier.Stop();
_notifier.Dispose();
}
}
}
现在我们已经设置了数据库记录更改通知,是时候实现Blazor组件了。第一步,我们检索OnInitialized()方法中的所有当前股价,然后我们订阅有关表记录更改的事件通知,以刷新HTML视图:
代码语言:javascript复制@page "/"
@using BlazorApp1.Models
@using BlazorApp1.Service
@inject ITableChangeBroadcastService StockService
@implements IDisposable
<h1>Stock prices</h1>
<p>Immediate client notification on record table change with Blazor</p>
<table class="table">
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
@foreach (var stock in stocks)
{
<tr>
<td>@stock.Code</td>
<td>@stock.Name</td>
<td>@stock.Price</td>
</tr>
}
</tbody>
</table>
@code {
IList<Stock> stocks;
protected override void OnInitialized()
{
// Subscription to table record change events
this.StockService.OnStockChanged = this.StockChanged;
this.stocks = this.StockService.GetCurrentValues();
}
// The event handler, will update the HTML view according to new stock value
private async void StockChanged(object sender, StockChangeEventArgs args)
{
var recordToupdate = this.stocks.FirstOrDefault(x => x.Code == args.NewValue.Code);
if (recordToupdate == null)
{
this.stocks.Add(args.NewValue);
}
else
{
recordToupdate.Price = args.NewValue.Price;
}
await InvokeAsync(() =>
{
base.StateHasChanged();
});
}
public void Dispose()
{
this.StockService.OnStockChanged -= this.StockChanged;
}
}
表格记录更改事件处理程序仅检查库存是否在显示的列表中,然后插入或更新其Price值。请注意,HTML将从Blazor自动刷新。为了更新HTML视图内容,我们不需要向浏览器发送任何通知,也不需要从浏览器向服务器发出任何轮询请求。
总而言之,我们将依赖性解析定义为单例:
代码语言:javascript复制namespace BlazorApp1
{
public class Startup
{
…
…
public void ConfigureServices(IServiceCollection services)
{
…
services.AddSingleton<ITableChangeBroadcastService, TableChangeBroadcastService>();
…
}
}
而且…别忘了设置数据库连接字符串!
代码语言:javascript复制{
"ConnectionString": "Data Source=***; initial catalog=***; User ID=sa;Password=***"
}