You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 12:40:12