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

Blazor Server页面API数据定时刷新UI失效问题求助

Blazor定时器更新数据后UI不刷新的解决方案

核心问题分析

你的代码中使用System.Timers.Timer的Elapsed事件触发异步操作,存在两个关键问题:

  • async void的事件处理程序会导致线程上下文管理混乱,异常无法被正确捕获
  • 直接替换List<Horse>实例可能让Blazor的变更检测无法及时识别数据变化

修复后的完整代码

@page "/"
@using DerbyStonks.Blazor.Services.Derby.Entities
@using System.Threading
@inject IHttpClientFactory ClientFactory
@inject NavigationManager NavigationManager
@implements IDisposable

<PageTitle>Home</PageTitle>

<br/>

<h1>Horses</h1>

@if (_loading)
{
    <h3>Loading ...</h3>
}
else
{
    <ul class="horse-list">
        @foreach (Horse horse in _horses)
        {
            <li class="horse-list-item">
                <h2>#@horse.Number: @horse.Name</h2>
            </li>
        }
    </ul>
}

@code
{
    private Timer _timer;
    private bool _loading = true;
    private List<Horse> _horses = new List<Horse>();
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

    protected override async Task OnInitializedAsync()
    {
        await LoadHorses();
        StartHotReload();
    }

    private void StartHotReload()
    {
        _timer = new Timer(async _ => 
        {
            await LoadHorsesAndUpdateUI();
        }, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
    }

    private async Task LoadHorsesAndUpdateUI()
    {
        if (!await _semaphore.WaitAsync(TimeSpan.Zero))
        {
            return;
        }

        try
        {
            await LoadHorses();
            await InvokeAsync(() => StateHasChanged());
        }
        catch (Exception ex)
        {
            // 添加日志记录逻辑
        }
        finally
        {
            _semaphore.Release();
        }
    }

    private async Task LoadHorses()
    {
        HttpClient client = ClientFactory.CreateClient();
        client.BaseAddress = new Uri(NavigationManager.BaseUri);

        _loading = true;
        try
        {
            var horses = await client.GetFromJsonAsync<List<Horse>>("api/Horses");
            if (horses != null)
            {
                _horses.Clear();
                _horses.AddRange(horses);
            }
            else
            {
                _horses.Clear();
            }
        }
        finally
        {
            _loading = false;
        }
    }

    public void Dispose()
    {
        _timer?.Dispose();
        _semaphore?.Dispose();
    }
}

关键修改说明

  1. 替换定时器类型:使用System.Threading.Timer替代System.Timers.Timer,避免async void事件处理带来的线程上下文问题
  2. 显式实现IDisposable:添加@implements IDisposable,确保组件销毁时定时器和信号量能被正确释放
  3. 并发请求控制:用SemaphoreSlim防止短时间内多个API请求同时执行,避免状态冲突
  4. 优化数据更新逻辑:将直接替换_horses实例改为清空后添加新数据,让Blazor变更检测更容易识别集合变化
  5. 明确状态变更调用:通过InvokeAsync(() => StateHasChanged())确保状态变更通知在UI线程执行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 10:35:00