Blazor WASM中API控制器无法触发Razor组件EventHandler
问题分析与解决方案
核心问题
你遇到的问题本质是Blazor WASM客户端与服务端的Scoped服务是完全独立的实例:服务端API控制器注入的ITestMartInterface是服务端进程内的实例,而客户端Razor组件订阅的是浏览器进程内的客户端实例,两者没有任何关联。服务端触发的事件只会在自己的实例上执行,客户端组件根本接收不到。
正确解决方案:使用SignalR实现服务端到客户端的实时推送
Blazor WASM是纯客户端运行的应用,服务端无法直接调用客户端内存中的对象或事件,必须通过跨进程实时通讯机制实现,SignalR是官方推荐的标准方案。
步骤1:服务端配置SignalR
- 安装SignalR包:
Install-Package Microsoft.AspNetCore.SignalR
- 创建SignalR Hub类:
public class UpdateHub : Hub { // 无需额外方法,仅作为服务端与客户端的通讯端点 }
- 在服务端
Program.cs中注册并映射Hub:
// 注册SignalR服务 builder.Services.AddSignalR(); // 映射Hub路由 app.MapHub<UpdateHub>("/updateHub");
- 修改API控制器,注入
IHubContext实现推送:
private readonly IHubContext<UpdateHub> _hubContext; public AtsMessageReceivedController(IHubContext<UpdateHub> hubContext) { _hubContext = hubContext; } [HttpGet] // 替换async void为async Task,避免异步内存泄漏 public async Task Get() { // 向所有连接的客户端推送更新通知 await _hubContext.Clients.All.SendAsync("ReceiveUpdate"); }
步骤2:客户端配置SignalR
- 安装客户端SignalR包:
Install-Package Microsoft.AspNetCore.SignalR.Client
- 创建客户端SignalR服务,封装连接与事件订阅:
public interface IUpdateSignalRService { event EventHandler UpdateReceived; Task StartConnectionAsync(); } public class UpdateSignalRService : IUpdateSignalRService, IAsyncDisposable { private readonly HubConnection _hubConnection; public event EventHandler UpdateReceived; public UpdateSignalRService() { // 替换为你的服务端实际地址 var baseUrl = "https://your-server-address"; _hubConnection = new HubConnectionBuilder() .WithUrl($"{baseUrl}/updateHub") .Build(); // 订阅服务端推送的消息 _hubConnection.On("ReceiveUpdate", () => { UpdateReceived?.Invoke(this, EventArgs.Empty); }); } public async Task StartConnectionAsync() { if (_hubConnection.State == HubConnectionState.Disconnected) { await _hubConnection.StartAsync(); } } public async ValueTask DisposeAsync() { await _hubConnection.StopAsync(); await _hubConnection.DisposeAsync(); } }
- 在客户端
Program.cs注册该服务:
builder.Services.AddScoped<IUpdateSignalRService, UpdateSignalRService>();
步骤3:客户端Razor组件订阅事件
@inject IUpdateSignalRService signalRService @implements IAsyncDisposable @code { protected override async Task OnInitializedAsync() { // 启动SignalR连接 await signalRService.StartConnectionAsync(); // 订阅更新事件 signalRService.UpdateReceived += DoStuff; } public async void DoStuff(object sender, EventArgs e) { // 执行你的业务代码 // 如果需要更新UI,需手动调用StateHasChanged StateHasChanged(); } // 组件销毁时取消订阅并释放资源 public async ValueTask DisposeAsync() { signalRService.UpdateReceived -= DoStuff; await ((IAsyncDisposable)signalRService).DisposeAsync(); } }
关键注意事项
- 移除原有的跨端共享
ITestMartInterface的逻辑,因为两端实例完全隔离,无法实现跨进程事件触发。 - 处理SignalR连接的异常与重连逻辑(可在
UpdateSignalRService中添加Closed事件监听实现自动重连)。 - 组件销毁时必须取消事件订阅并释放SignalR连接,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Mart Apon
相关产品推荐
相关产品推荐

