Blazor中如何在不同页面复用已有的SignalR HubConnection
Blazor客户端共享SignalR HubConnection的解决办法
方案1:用Scoped包装类统一管理
IAccessTokenProvider是Scoped生命周期服务,无法直接在Singleton服务中注入(生命周期不兼容),因此可以创建Scoped包装类来管理HubConnection的创建与共享:
步骤1:创建HubConnection包装服务
public class HubConnectionService { public HubConnection HubConnection { get; } public HubConnectionService(NavigationManager navigationManager, IAccessTokenProvider accessTokenProvider) { HubConnection = new HubConnectionBuilder() .WithUrl(navigationManager.ToAbsoluteUri("/hub"), options => { options.AccessTokenProvider = async () => { var accessTokenResult = await accessTokenProvider.RequestAccessToken(); accessTokenResult.TryGetToken(out var accessToken); return accessToken?.Value; }; }) .WithAutomaticReconnect() .Build(); } }
步骤2:注册Scoped服务
在Program.cs中把包装类注册为Scoped服务:
builder.Services.AddScoped<HubConnectionService>();
步骤3:在MainLayout中启动连接
在MainLayout的初始化方法中启动连接,确保全局只初始化一次:
@inject HubConnectionService HubConnectionService protected override async Task OnInitializedAsync() { if (HubConnectionService.HubConnection.State == HubConnectionState.Disconnected) { await HubConnectionService.HubConnection.StartAsync(); } }
步骤4:在任意页面中注入使用
直接在需要的页面注入包装类,就能拿到共享的连接实例:
@inject HubConnectionService HubConnectionService @code { protected override async Task OnInitializedAsync() { // 注册Hub消息监听 HubConnectionService.HubConnection.On<string>("ReceiveMessage", message => { // 处理收到的消息 StateHasChanged(); }); } // 发送消息示例 private async Task SendMessage() { await HubConnectionService.HubConnection.SendAsync("SendMessage", "测试内容"); } }
方案2:通过MainLayout用CascadingValue传递(适合小型项目)
如果项目规模小,不想额外创建服务,可直接在MainLayout中通过CascadingValue把连接实例传递给所有子页面:
步骤1:在MainLayout中配置CascadingValue
@inject NavigationManager NavigationManager @inject IAccessTokenProvider AccessTokenProvider <CascadingValue Value="_hubConnection" Name="HubConnection"> @Body </CascadingValue> @code { private HubConnection _hubConnection; protected override async Task OnInitializedAsync() { _hubConnection = new HubConnectionBuilder() .WithUrl(NavigationManager.ToAbsoluteUri("/hub"), options => { options.AccessTokenProvider = async () => { var accessTokenResult = await AccessTokenProvider.RequestAccessToken(); accessTokenResult.TryGetToken(out var accessToken); return accessToken?.Value; }; }) .WithAutomaticReconnect() .Build(); if (_hubConnection.State == HubConnectionState.Disconnected) { await _hubConnection.StartAsync(); } } }
步骤2:在页面中接收连接实例
[CascadingParameter(Name = "HubConnection")] private HubConnection HubConnection { get; set; } @code { protected override async Task OnInitializedAsync() { if (HubConnection != null) { HubConnection.On<string>("ReceiveMessage", message => { // 处理消息逻辑 StateHasChanged(); }); } } }
关键注意事项
- 生命周期匹配:必须用Scoped管理HubConnection,避免和Scoped的
IAccessTokenProvider生命周期冲突 - 自动重连:添加
.WithAutomaticReconnect()可提升连接稳定性,断开后自动重试 - 状态检查:使用前务必检查连接状态,避免在断开状态下发送消息
- 资源释放:可在MainLayout中实现
IDisposableAsync接口,页面销毁时关闭连接:
public async ValueTask DisposeAsync() { if (_hubConnection != null) { await _hubConnection.DisposeAsync(); } }
内容的提问来源于stack exchange,提问作者Igor Markiv
相关产品推荐
相关产品推荐

