如何通过SignalR获取Controller调用Background Service后的延迟响应?
.NET5 后台服务完成后通过SignalR推送数据实现方案
实现步骤
1. 定义并注册SignalR Hub
首先创建专用的对比结果推送Hub,无需编写业务方法,仅做事件推送的载体即可:
public class CompareResultHub : Hub { // 客户端只需连接该Hub,监听receiveUpdatedText事件即可 }
在Startup.cs中注册SignalR服务和端点:
public void ConfigureServices(IServiceCollection services) { // 原有服务注册保留,新增以下内容 services.AddSignalR(); services.AddHttpContextAccessor(); // 后续用于获取当前请求用户ID } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 原有中间件配置保留,在端点映射部分新增 app.UseEndpoints(endpoints => { endpoints.MapHub<CompareResultHub>("/compareResultHub"); // 原有Controller等端点保留 }); }
2. 修改传输Dto,携带请求用户标识
后台服务需要知道要把结果推给哪个用户,所以需要给VersionDto新增UserId属性:
public class VersionDto { // 原有InitialDraftId、DraftId等属性保留 public string UserId { get; set; } public VersionDto(Guid initialDraftId, Guid draftId, int currVer, int prevVer, string userId) { // 原有属性赋值保留 UserId = userId; } }
修改GetComparedTextHandler,注入IHttpContextAccessor获取当前请求的用户ID,构造VersionDto时传入:
public class GetComparedTextHandler : IRequestHandler<GetComparedTextQuery, ComparedDraftDto> { private readonly AppDbContext _appDbContext; private readonly TpaChannel _tpaChannel; private readonly IHttpContextAccessor _httpContextAccessor; public GetComparedTextHandler(AppDbContext appDbContext, TpaChannel tpaChannel, IHttpContextAccessor httpContextAccessor) { _appDbContext = appDbContext; _tpaChannel = tpaChannel; _httpContextAccessor = httpContextAccessor; } public async Task<ComparedDraftDto> Handle(GetComparedTextQuery request, CancellationToken cancellationToken) { // 原有逻辑保留 var edDraftComparison = ...; if (edDraftComparison == null) { // 原有入库、创建Channel逻辑保留 // 获取当前请求用户ID var userId = _httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier); // 构造VersionDto时传入用户ID var versionDto = new VersionDto(request.InitialDraftId, request.DraftId, request.CurrVer, request.PrevVer, userId); await _tpaChannel.AddVersionsAsync(versionDto, cts.Token); } // 原有返回逻辑保留 } }
3. 后台服务注入IHubContext,任务完成后推送数据
直接在你处理Channel任务的BackgroundService中注入IHubContext<CompareResultHub>,待仓库记录更新完成后调用推送方法即可:
public class TpaBackgroundService : BackgroundService { private readonly TpaChannel _tpaChannel; private readonly AppDbContext _appDbContext; private readonly IHubContext<CompareResultHub> _hubContext; public TpaBackgroundService(TpaChannel tpaChannel, AppDbContext appDbContext, IHubContext<CompareResultHub> hubContext) { _tpaChannel = tpaChannel; _appDbContext = appDbContext; _hubContext = hubContext; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // 原有读取Channel任务的逻辑保留 await foreach (var versionDto in _tpaChannel.ReadAllAsync(stoppingToken)) { // 原有更新仓库记录的逻辑执行完成后,新增推送逻辑 if (!string.IsNullOrEmpty(versionDto.UserId)) { await _hubContext.Clients.User(versionDto.UserId).SendAsync( "receiveUpdatedText", new { DraftId = versionDto.DraftId, // 填充你需要返回的最新对比结果字段 }, stoppingToken ); } } } }
补充说明
- 若你使用自定义的用户身份体系,也可以将用户标识替换为SignalR连接ID,需要客户端连接Hub时上报用户ID,在Hub中维护用户ID和连接ID的映射关系,后台服务通过
Clients.Client(connectionId)推送即可。 - React客户端只需正常连接
/compareResultHub端点,监听receiveUpdatedText事件即可拿到更新后的对比数据。
内容的提问来源于stack exchange,提问作者hdoitc
相关产品推荐
相关产品推荐

