Asp.Net Core 8.0分层架构中SignalR与SqlDependency使用咨询
Asp.Net Core 8.0分层架构中SignalR+SqlDependency实现方案
一、能否与Entity Framework或Dapper ORM配合使用?
完全可以,两种ORM都能适配SqlDependency的使用场景:
- EF Core:可以通过
FromSqlRaw执行原生查询来关联SqlDependency,或者直接获取EF上下文底层的SqlConnection对象进行操作。需要注意EF上下文的生命周期,确保监听期间连接状态有效,或者在SqlDependency触发后重新建立监听。 - Dapper:作为轻量ADO.NET封装,直接基于
SqlConnection创建SqlDependency的方式和原生ADO.NET完全一致,灵活性更高,不需要额外适配。
二、分层架构下的具体实现步骤
1. 前置准备:数据库与EntityLayer
- 确保SQL Server数据库开启Service Broker,执行SQL命令:
ALTER DATABASE [你的数据库名] SET ENABLE_BROKER; - 在EntityLayer定义与数据库表对应的实体类,比如
Order、Product等。
2. DataAccessLayer(DAL)实现
- 定义仓储接口,包含数据查询和SqlDependency注册方法:
public interface IRealTimeRepository<T> where T : class { Task<IEnumerable<T>> GetAllAsync(); void RegisterDataChangeNotification(Action<SqlNotificationEventArgs> onChange); } - 实现仓储类(以Dapper为例):
public class RealTimeRepository<T> : IRealTimeRepository<T> { private readonly string _connStr; public RealTimeRepository(IConfiguration config) { _connStr = config.GetConnectionString("DefaultConnection"); } public async Task<IEnumerable<T>> GetAllAsync() { using var conn = new SqlConnection(_connStr); return await conn.QueryAsync<T>($"SELECT * FROM {typeof(T).Name}s"); } public void RegisterDataChangeNotification(Action<SqlNotificationEventArgs> onChange) { using var conn = new SqlConnection(_connStr); conn.Open(); using var cmd = new SqlCommand($"SELECT Id FROM {typeof(T).Name}s", conn); cmd.Notification = null; var dependency = new SqlDependency(cmd); dependency.OnChange += (sender, e) => { onChange.Invoke(e); // 重新注册监听,因为SqlDependency触发一次后自动失效 RegisterDataChangeNotification(onChange); }; cmd.ExecuteNonQuery(); } }
3. BusinessLayer(BLL)实现
- 定义业务服务接口,封装数据转换和事件通知逻辑:
public interface IRealTimeDataService { Task<IEnumerable<YourDto>> GetLatestDataAsync(); event Action OnDataChanged; // 定义事件供API层Hub订阅 } - 实现业务服务,关联DAL并触发事件:
public class RealTimeDataService : IRealTimeDataService { private readonly IRealTimeRepository<YourEntity> _repo; private readonly IMapper _mapper; public event Action OnDataChanged; public RealTimeDataService(IRealTimeRepository<YourEntity> repo, IMapper mapper) { _repo = repo; _mapper = mapper; // 注册SqlDependency监听 _repo.RegisterDataChangeNotification(e => { if (e.Type == SqlNotificationType.Change) { OnDataChanged?.Invoke(); } }); } public async Task<IEnumerable<YourDto>> GetLatestDataAsync() { var entities = await _repo.GetAllAsync(); return _mapper.Map<IEnumerable<YourDto>>(entities); } }
4. DtosLayer
- 定义与实体类对应的DTO,比如
YourDto,用于API和UI层数据传输,避免直接暴露实体结构。
5. ApiLayer(SignalR Hub + 接口配置)
- 安装
Microsoft.AspNetCore.SignalRNuGet包 - 创建SignalR Hub类,订阅BLL事件并推送数据给客户端:
public class DataUpdateHub : Hub { private readonly IRealTimeDataService _dataService; public DataUpdateHub(IRealTimeDataService dataService) { _dataService = dataService; _dataService.OnDataChanged += async () => { var latestData = await _dataService.GetLatestDataAsync(); await Clients.All.SendAsync("ReceiveDataUpdate", latestData); }; } } - 在
Program.cs中配置SignalR和依赖注入:// 注册服务 builder.Services.AddSignalR(); builder.Services.AddScoped(typeof(IRealTimeRepository<>), typeof(RealTimeRepository<>)); builder.Services.AddScoped<IRealTimeDataService, RealTimeDataService>(); builder.Services.AddAutoMapper(typeof(Program)); // 配置中间件 app.MapHub<DataUpdateHub>("/dataUpdateHub"); // 可选:提供REST接口供UI主动拉取数据 app.MapGet("/api/realtime/data", async (IRealTimeDataService service) => { var data = await service.GetLatestDataAsync(); return Results.Ok(data); });
6. UI Layer(MVC)实现
- 安装
Microsoft.AspNetCore.SignalR.ClientNuGet包 - 在视图中引入SignalR客户端脚本,并编写连接逻辑:
<div id="dataList"></div> <script src="~/lib/signalr/dist/browser/signalr.js"></script> <script> // 建立与API层Hub的连接 const connection = new signalR.HubConnectionBuilder() .withUrl("/dataUpdateHub") .build(); // 接收API推送的更新数据 connection.on("ReceiveDataUpdate", (data) => { renderData(data); }); // 页面加载时主动拉取初始数据 async function loadInitialData() { const res = await fetch("/api/realtime/data"); const data = await res.json(); renderData(data); } // 渲染数据到页面 function renderData(data) { let html = "<ul>"; data.forEach(item => { html += `<li>ID: ${item.Id} | 名称: ${item.Name}</li>`; }); html += "</ul>"; document.getElementById("dataList").innerHTML = html; } // 启动连接 connection.start().catch(err => console.error(err)); loadInitialData(); </script>
内容的提问来源于stack exchange,提问作者Gökmen Ada
相关产品推荐
相关产品推荐

