首页 > 解决方案 > 不同的线程从 Microsoft.Extensions.DependencyInjection 获得相同的 DbContext

问题描述

我有一个带有视图模型的 WPF 应用程序,它调用数据访问类来完成数据库工作。DataAccess 类具有所有异步函数,VM 使用 await _dataAccess.DoWork(withItem); 有时我会收到一个错误,即在同一上下文中执行两个操作。除非服务提供者在对 GetService 的不同调用中使用相同的上下文,否则这是不可能的。

在 App.xaml.cs 中

services.AddDbContext<MyItemDbContext>(options =>
{
    options.UseLoggerFactory(_loggerFactory);
    options.UseSqlServer(config.GetConnectionString("My_Items_ConnectionString"));
});
// register concrete class for IDataAccess
services.AddScoped<IDataAccess, ItemDataAccess>();

在查看代码

private async void BtnReady_Click(object sender, RoutedEventArgs e)
{
    await SetStatusForSelectedItem(StatusIds.Ready);
}
private async Task SetStatusForSelectedItems(StatusIds statusId)
{
    // get selected itemDetail item from grid
    await _mainWindowVM.UpdateItemDetailState(itemDetail, stateId);
}

在 ViewModel 代码中

private readonly IDataAccess _dataAccess;
public MainWindowVM(IDataAccess dataAccess)
{
    _dataAccess = dataAccess;
}
public async Task UpdateItemDetailState(Item item, StatusIds stateId)
{
    // other necessary code
    item.StateId = stateId;
    await _dataAccess.UpdateItem(item);
}

在 DataAccess (DalBase) 代码中

private readonly IServiceProvider _serviceProvider;
public DalBase(IServiceProvider serviceProvider)
{
    _serviceProvider = serviceProvider;
}

protected MyItemDbContext Get_Item_DbContext()
{
    return _serviceProvider.GetService<MyItemDbContext>();
}
// other contexts for different purposes

在 DataAccess 中(派生)

public ItemDataAccess(IServiceProvider serviceProvider) : base(serviceProvider) { }

public async Task UpdateItem(Item item)
{
    MyItemDbContext db = Get_Item_DbContext(); // <<< === should be new instance each time, right?
    db.Items.Update(item);
    await db.SaveChangesAsync();
}

我一直在遵循这种模式,只是我尝试了很多...catch 来减少尺寸。Get_Item_DbContext()DataAccess 中的每个函数都以我曾经拥有的调用开始,using (var db = Get_Item_DbContext())但我读过一篇文章说让依赖注入决定生命周期。我找不到没有在异步函数上使用 await 的任何地方,但我收到这样的错误...

在前一个操作完成之前,在此上下文上启动了第二个操作...

有一个操作在列表中运行,它调用这个更新状态函数,还有一个用户可以用来调用相同函数的按钮。那是我得到错误的时候。

编辑我曾经有,using (var db = Get_Item_DbContext)但后来我得到了一个不同的错误......无法访问已处理的上下文实例。

更新我尝试将上下文注入 DalBase。单击运行第二个操作时,我仍然收到错误消息。在这种情况下,这可能是因为这两个操作都通过同一个 VM 运行,该 VM 现在具有一个 _dataAccess 和一个注入的上下文。我试图通过从每个函数中获取一个新实例来避免这种情况。

所以我的问题是这些我怎样才能找出哪些线程(及其调用堆栈)正在获得相同的上下文?为什么两个单独的线程(来自异步调用)从 GetService 获取相同的上下文?是否有我必须添加到 AddDbContext 以使其范围正确的选项?

谢谢迈克

标签: c#entity-frameworkmvvmdependency-injectionasync-await

解决方案


我建议停止在类中注入 ServiceProvider 并获得这样的服务。我称之为反模式(服务定位器)。而是直接在类中注入您的依赖项。

所以与其:

在 DataAccess (DalBase) 代码中

private readonly MyItemDbContext _myItemDbContext;
public DalBase(MyItemDbContext myItemDbContext)
{
    _myItemDbContext = myItemDbContext;
}

// Don't expose the myItemDbContext from this class. Just use it to what you need

// other contexts for different purposes

在 DataAccess 中(派生)

private readonly MyItemDbContext _myItemDbContext;
public ItemDataAccess(MyItemDbContext myItemDbContext)
{
    _myItemDbContext = myItemDbContext;
}

public async Task UpdateItem(Item item)
{
    _myItemDbContext.Items.Update(item);
    await _myItemDbContext.SaveChangesAsync();
}

如果您不将 MyItemDbContext 的使用暴露给其他类,我认为您的问题将会消失。


推荐阅读