c# - 不同的线程从 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 以使其范围正确的选项?
谢谢迈克
解决方案
我建议停止在类中注入 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 的使用暴露给其他类,我认为您的问题将会消失。
推荐阅读
- android - Kotlin Parcelize 中的意外行为
- php - 制作一个允许除@符号之外的所有电子邮件字符的正则表达式
- php - PHP获得下个月的继续日期并每月获得价格贷款,第一个月支付利息
- javascript - 显示文本颜色基于 json 颜色代码 0xAF001D
- javascript - Vue js在单击按钮时动态添加文本框时绑定动态ID
- spring - spring boot servlet上下文与应用程序上下文
- amazon-s3 - 我一直在尝试使用 logrotate 实用程序并将压缩日志移动到 s3,但是,它仅在强制完成时才会旋转
- powershell - 使用 powershell 将用户添加到 Exchange In-placeHold/eDiscovery (In-Situ)
- java - 从 0 数到 100
- php - 使用 Laravel 5.8 获取所有本地化字符串