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

NET 8 SignalR HubConnection报错:无法访问已释放对象

SignalR重连时出现“Cannot access a disposed object”错误的原因及修复方案

问题根源分析

  1. Closed事件绑定时机滞后
    你在StartAsync执行完成后才绑定Closed事件,若StartAsync初始化失败直接触发连接关闭,此时事件处理器尚未注册,无法触发重连逻辑;即便初始连接成功,后续重连逻辑也存在对象引用失效的问题。

  2. 已释放的HubConnection实例被重复调用
    当Blazor组件被销毁(如页面刷新、导航跳转),DisposeAsync会释放_myHubConnection实例,但如果之前的连接断开事件延迟触发,ReconnectAsync会尝试调用已释放对象的StartAsync方法,直接抛出“已释放对象”错误。

  3. Hub端无限循环未处理连接中断
    StreamData方法中的while(true)未监听连接终止信号,当客户端断开时,SendAsync会抛出异常,导致Hub方法异常终止,进而触发客户端连接关闭事件。

  4. 重连逻辑未重新创建连接实例
    HubConnection被释放后无法通过StartAsync重新激活,必须创建全新的实例才能完成重连。

修复方案

1. 调整连接创建与事件绑定逻辑

修改Page.cs中的连接初始化和重连方法,确保每次重连都创建新实例,并提前绑定Closed事件:

@page "/"

<PageTitle>Home</PageTitle>

@inject NavigationManager Navigation
@using Microsoft.AspNetCore.SignalR.Client
@implements IAsyncDisposable

<h2 class="title">Dashboard</h2>

<div class="container">
    <div class="card">
        <h3>Data</h3>
        <p class="value">@MyData</p>
    </div>
</div>

@code {
    private HubConnection? _myHubConnection;
    private int MyData = 0;

    protected override async Task OnInitializedAsync()
    {
        await CreateAndStartHubConnection();
    }

    private async Task CreateAndStartHubConnection()
    {
        // 每次创建全新的连接实例
        _myHubConnection = new HubConnectionBuilder()
            .WithUrl(Navigation.ToAbsoluteUri("/myHub"))
            .Build();

        _myHubConnection.On<int>("ReceiveData", async (data) =>
        {
            MyData = data;
            Console.WriteLine($"Received data: {data}");
            await InvokeAsync(StateHasChanged);
        });

        // 提前绑定Closed事件,确保连接异常时能触发重连
        _myHubConnection.Closed += async (exception) =>
        {
            Console.WriteLine($"Connection closed: {exception?.Message}");
            await ReconnectAsync();
        };

        try
        {
            await _myHubConnection.StartAsync();
            await _myHubConnection.SendAsync("StreamData");
            Console.WriteLine("Connection started successfully");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Failed to start connection: {ex.Message}");
            await ReconnectAsync();
        }
    }

    private async Task ReconnectAsync()
    {
        // 避免重复触发重连逻辑
        if (_myHubConnection?.State != HubConnectionState.Disconnected)
            return;

        while (true)
        {
            try
            {
                // 重新创建连接实例并启动
                await CreateAndStartHubConnection();
                break; // 重连成功后退出循环
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Reconnect failed: {ex.Message}");
                await Task.Delay(5000);
            }
        }
    }

    public async ValueTask DisposeAsync()
    {
        Console.WriteLine("Disposing called!");
        if (_myHubConnection != null)
        {
            // 取消Closed事件订阅,防止组件销毁后仍触发重连
            _myHubConnection.Closed -= ReconnectAsync;
            await _myHubConnection.DisposeAsync();
            _myHubConnection = null;
        }
    }
}

2. 修改Hub端方法,处理连接中断信号

在Hub.cs中,使用Context.ConnectionAborted监听连接终止,避免无限循环报错:

using Microsoft.AspNetCore.SignalR;

public class MyHub : Hub
{
    public async Task StreamData()
    {
        var random = new Random();
        // 监听连接中断信号,优雅终止循环
        while (!Context.ConnectionAborted.IsCancellationRequested)
        {
            var temp = random.Next(-10, 35);
            await Clients.All.SendAsync("ReceiveData", temp, cancellationToken: Context.ConnectionAborted);
            await Task.Delay(1000, Context.ConnectionAborted);
        }
    }
}

额外注意事项

  • Blazor Server模式下,需注意组件生命周期,避免因组件重新渲染重复创建连接;
  • 可添加连接状态的UI提示,提升用户体验;
  • 生产环境建议增加重连次数限制,避免无限循环消耗资源。

内容的提问来源于stack exchange,提问作者Jebathon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 19:37:32