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

Blazor server端客户端关闭或离开页面时持续抛出System.ArgumentNullException异常

异常原因

该ArgumentNullException确实由客户端断开连接后定时器仍在运行导致:Blazor Server的页面组件与客户端通过SignalR Circuit绑定,当用户关闭窗口或导航离开时,Circuit会被销毁释放,但未手动停止的定时器仍会持续执行,调用StateHasChanged()或JS交互逻辑时依赖的上下文对象为空,就会抛出该异常。

修复方案

你只需要在页面组件中实现IAsyncDisposable接口,在组件销毁时自动释放定时器即可,Blazor会在用户离开页面时自动调用该接口的方法,无需额外编写客户端导航检测逻辑。
完整示例代码如下:

@implements IAsyncDisposable

<!-- 你的页面渲染代码 -->

@code {
    private Timer _autoUpdateTimer;
    private readonly CancellationTokenSource _destroyCts = new();

    protected override void OnInitialized()
    {
        // 初始化每秒执行的定时器
        _autoUpdateTimer = new Timer(TimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
    }

    private async void TimerCallback(object state)
    {
        // 组件已销毁则直接终止逻辑
        if (_destroyCts.IsCancellationRequested) return;

        try
        {
            // 你的业务更新逻辑
            // 注意:UI更新必须包裹在InvokeAsync中执行
            await InvokeAsync(StateHasChanged);
        }
        catch (ArgumentNullException)
        {
            // 捕获到断开导致的空异常时直接销毁资源
            await DisposeAsync();
        }
        catch (OperationCanceledException)
        {
            // 取消操作直接忽略
        }
    }

    public async ValueTask DisposeAsync()
    {
        // 触发取消令牌,终止所有运行中的逻辑
        await _destroyCts.CancelAsync();
        
        // 释放定时器
        if (_autoUpdateTimer != null)
        {
            await _autoUpdateTimer.DisposeAsync();
            _autoUpdateTimer = null;
        }

        _destroyCts.Dispose();
    }
}
可选优化

如果你使用的是.NET 6及以上版本,可使用PeriodicTimer替代传统Timer,资源管理更简洁:

private PeriodicTimer _periodicTimer;

protected override async Task OnInitializedAsync()
{
    _periodicTimer = new PeriodicTimer(TimeSpan.FromSeconds(1));
    // 启动后台定时任务
    _ = RunAutoUpdateTask();
}

private async Task RunAutoUpdateTask()
{
    try
    {
        // 等待定时器触发,组件销毁时令牌会自动取消循环
        while (await _periodicTimer.WaitForNextTickAsync(_destroyCts.Token))
        {
            // 业务逻辑 + UI更新
            await InvokeAsync(StateHasChanged);
        }
    }
    catch (OperationCanceledException)
    {
        // 忽略销毁触发的取消异常
    }
}
注意事项
  • 所有跨线程的UI更新操作必须包裹在InvokeAsync中执行,避免线程安全问题。
  • 无需额外实现客户端断开监听逻辑,Blazor的Circuit生命周期会自动触发组件的DisposeAsync方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 10:36:02