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

WPF中AsyncCommand触发后UI整体冻结问题求助

WPF UI冻结问题:AsyncCommand与EF Core异步查询优化

问题描述

我开发的简易应用通过MediatR实现DataGrid数据加载功能,参考微软官方文档实现了AsyncCommand来完成按钮点击刷新数据的逻辑。但触发RefreshBtnCommand后,每次重载数据或切换页面时,整个UI都会冻结数秒,需要排查问题根源并解决。

相关代码

ViewModel代码

public class MainPageViewModel : BindableBase
{
    public IAsyncRelayCommand DownloadTextCommand { get; private set; }
    private readonly IRepositoryManager _RepositoryManager;
    public MainPageViewModel(IRepositoryManager RepositoryManager)
    {
        RefreshBtnCommand = new AsyncRelayCommand(runTest);
    }
    public async Task runTest()
    {
            await Task.Run(() => {
                int i = 0;
                while (true) {
                    if (i++ == int.MaxValue) break;
                }
            });

            await _RepositoryManager.TestRepository.Test();
    }
}

XAML代码

<Grid>
  <Button x:Name="button" Command="{Binding DownloadTextCommand}"/>
</Grid>

Repository代码

public class TestRegisterRepository : ITestRegisterRepository
{
    private new readonly ModelContext _modelContext;
    public TestRegisterRepository (ModelContext repositoryContext)
    {
        _modelContext = repositoryContext;
    }

    public async Task<List<DataTable>> Test() {
        return await Task.Run(() => this._modelContext.DataTable.Take(1000).ToListAsync());
//return await this._modelContext.DataTable.Take(1000).ToListAsync(); //Freeze UI
    }
}

问题根源分析

  1. 命令绑定不匹配:ViewModel中初始化的是RefreshBtnCommand,但公开的属性是DownloadTextCommand,XAML绑定的也是DownloadTextCommand,导致命令无法正确触发,后续错误逻辑可能阻塞UI线程。
  2. 无意义的线程阻塞:runTest方法里的Task.Run循环完全是冗余操作,会占用后台线程资源,间接影响UI响应效率。
  3. EF Core异步方法错误封装:ToListAsync本身就是EF Core提供的原生异步方法,用Task.Run包裹反而会额外创建线程;直接调用时如果DbContext被绑定到UI线程(比如WPF默认配置),异步方法会在UI线程执行,引发冻结。

解决方案

1. 修正命令绑定

统一ViewModel中的命令名称,确保XAML绑定与ViewModel属性一致:

public class MainPageViewModel : BindableBase
{
    public IAsyncRelayCommand RefreshBtnCommand { get; private set; }
    private readonly IRepositoryManager _repositoryManager;

    public MainPageViewModel(IRepositoryManager repositoryManager)
    {
        _repositoryManager = repositoryManager;
        RefreshBtnCommand = new AsyncRelayCommand(RunTest);
    }

    public async Task RunTest()
    {
        // 移除无意义的循环阻塞代码
        var data = await _repositoryManager.TestRepository.Test();
        // 将数据绑定到DataGrid(AsyncRelayCommand会自动切换回UI线程)
        // 示例:DataGridItems = new ObservableCollection<DataTable>(data);
    }
}

2. 正确使用EF Core异步查询

移除Task.Run包裹,直接调用原生异步方法,同时确保DbContext的DI配置正确:

public class TestRegisterRepository : ITestRegisterRepository
{
    private readonly ModelContext _modelContext;

    public TestRegisterRepository(ModelContext repositoryContext)
    {
        _modelContext = repositoryContext;
    }

    public async Task<List<DataTable>> Test()
    {
        return await _modelContext.DataTable.Take(1000).ToListAsync();
    }
}

3. 修正XAML绑定

<Grid>
  <Button x:Name="button" Command="{Binding RefreshBtnCommand}" Content="刷新数据"/>
</Grid>

额外优化建议

  • DbContext配置:确保DI中DbContext为范围服务(默认配置),避免单例导致的线程冲突:
    builder.Services.AddDbContext<ModelContext>(options =>
        options.UseSqlServer("你的数据库连接字符串"));
    
  • DataGrid虚拟化:开启DataGrid虚拟化,减少大量数据渲染时的UI压力:
    <DataGrid VirtualizingStackPanel.IsVirtualizing="True" 
              VirtualizingStackPanel.VirtualizationMode="Recycling"/>
    
  • 使用官方AsyncRelayCommand:确保引用Microsoft.Toolkit.Mvvm包,它会自动处理异步操作的线程切换,无需手动调用Dispatcher。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 14:54:49