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

WPF+Prism异步从SQL Server取数据绑定DataGrid界面不显示问题

Prism WPF异步加载数据到DataGrid无显示问题解决

问题场景

在Prism框架的WPF应用中,同步方式从SQL Server仓储层获取数据并绑定到DataGrid时,数据能正常显示;但切换为异步方式后,程序无报错,却无法在界面显示数据。尝试在ViewModel构造函数中调用异步私有方法加载数据,问题仍未解决。

相关代码

视图代码

<UserControl>
    <Grid>
        <DataGrid ItemsSource="{Binding Products}" />
    </Grid>
</UserControl>

ViewModel代码(原问题版本)

public class ProductViewModel : BindableBase, INavigationAware
{
    private readonly IProductRepo _repo = new ProductRepo();

    public ObservableCollection<Data.Models.Product> Products { get; set; }
    
    public ProductViewModel()
    {
        Products = new ObservableCollection<Data.Models.Product>(this._repo.GetAll().Result);
    }
}

DbContext代码

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext()
    {
    }

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
    {
    }

    public DbSet<Product> Products { get; set; }
}

IProductRepo接口

public interface IProductRepo
{
    Task<List<Product>> GetAll();
}

ProductRepo实现类

public class ProductRepo : IProductRepo
{
    private readonly ApplicationDbContext _context;

    public ProductRepo()
    {
        this._context = new ApplicationDbContext();
    }

    public async Task<List<Product>> GetAll()
    {
        return await this._context.Products.ToListAsync();
    }
}

尝试的加载方法

public void LoadData()
{
    List<Data.Models.Product> ProductFromDB = this._repo.GetAll().Result;

    foreach (var p in ProductFromDB)
    {
        Products.Add(p);
    }
}

问题原因

代码中使用_repo.GetAll().Result阻塞等待异步任务完成,会导致UI线程死锁:UI线程等待异步任务结束,而异步任务执行完成后默认会尝试回到原UI线程继续处理,但此时UI线程已被阻塞,任务无法完成,最终数据无法加载,界面无显示。

解决方法

1. 重构ViewModel异步加载逻辑

利用Prism的INavigationAware接口的异步OnNavigatedTo方法(Prism支持返回Task的异步版本),避免在构造函数中阻塞UI线程:

public class ProductViewModel : BindableBase, INavigationAware
{
    private readonly IProductRepo _repo;
    private ObservableCollection<Data.Models.Product> _products;

    public ObservableCollection<Data.Models.Product> Products
    {
        get => _products;
        set => SetProperty(ref _products, value);
    }

    // 推荐通过依赖注入获取仓储实例,而非直接new
    public ProductViewModel(IProductRepo repo)
    {
        _repo = repo;
        // 先初始化空集合,避免DataGrid绑定为空
        Products = new ObservableCollection<Data.Models.Product>();
    }

    public async void OnNavigatedTo(NavigationContext navigationContext)
    {
        // 异步获取数据,全程使用await
        var products = await _repo.GetAll();
        
        // 更新集合:清空现有数据并添加新数据
        Products.Clear();
        foreach (var product in products)
        {
            Products.Add(product);
        }
        // 也可以直接替换整个集合:Products = new ObservableCollection<Data.Models.Product>(products);
    }

    public bool IsNavigationTarget(NavigationContext navigationContext) => true;
    public void OnNavigatedFrom(NavigationContext navigationContext) { }
}

2. 移除阻塞式异步调用

删除所有使用.Result或.Wait()的代码,全程用await等待异步操作,这是避免死锁的核心。

3. 优化DbContext的依赖注入(推荐)

不要在ProductRepo中直接new ApplicationDbContext,通过依赖注入管理DbContext生命周期,避免资源泄漏:

public class ProductRepo : IProductRepo
{
    private readonly ApplicationDbContext _context;

    public ProductRepo(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<List<Product>> GetAll()
    {
        // 使用ConfigureAwait(false)避免强制回到UI线程,进一步降低死锁风险
        return await _context.Products.ToListAsync().ConfigureAwait(false);
    }
}

在Prism容器中注册DbContext和仓储(例如在App.xaml.cs的RegisterTypes方法):

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    // 注册SQL Server DbContext
    containerRegistry.RegisterSingleton<ApplicationDbContext>(() => 
        new ApplicationDbContext(new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseSqlServer("你的SQL Server连接字符串")
            .Options));
    
    // 注册仓储接口与实现
    containerRegistry.Register<IProductRepo, ProductRepo>();
    
    // 注册ViewModel等其他服务...
}

关键注意点

  • 全程异步:从仓储层到ViewModel,所有异步操作必须用await,禁止阻塞线程。
  • 导航生命周期:Prism的OnNavigatedTo是ViewModel初始化后的合适时机,适合异步加载数据,不会阻塞UI。
  • 属性通知:如果替换整个Products集合,需通过BindableBase的SetProperty触发PropertyChanged;如果仅修改集合内元素,ObservableCollection会自动触发集合变化通知。

内容的提问来源于stack exchange,提问作者Maurice Filiatreault Hebert

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 09:50:29