NET 8 SignalR HubConnection报错:无法访问已释放对象
SignalR重连时出现“Cannot access a disposed object”错误的原因及修复方案
问题根源分析
Closed事件绑定时机滞后
你在StartAsync执行完成后才绑定Closed事件,若StartAsync初始化失败直接触发连接关闭,此时事件处理器尚未注册,无法触发重连逻辑;即便初始连接成功,后续重连逻辑也存在对象引用失效的问题。已释放的HubConnection实例被重复调用
当Blazor组件被销毁(如页面刷新、导航跳转),DisposeAsync会释放_myHubConnection实例,但如果之前的连接断开事件延迟触发,ReconnectAsync会尝试调用已释放对象的StartAsync方法,直接抛出“已释放对象”错误。Hub端无限循环未处理连接中断
StreamData方法中的while(true)未监听连接终止信号,当客户端断开时,SendAsync会抛出异常,导致Hub方法异常终止,进而触发客户端连接关闭事件。重连逻辑未重新创建连接实例
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
相关产品推荐
相关产品推荐

