【发布时间】:2021-12-19 21:38:13
【问题描述】:
我有一个 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 来减小尺寸。 DataAccess 中的每个函数都以调用Get_Item_DbContext() 开始,我曾经拥有using (var db = Get_Item_DbContext()),但我读到的一篇文章说让依赖注入决定生命周期。我找不到任何我没有在异步函数上使用 await 的地方,但我收到这样的错误......
在前一个操作完成之前在此上下文上启动了第二个操作...
有一个操作在列表中运行,它调用了这个更新状态函数,还有一个按钮,用户可以使用它来调用相同的函数。那是我得到错误的时候。
编辑我曾经有using (var db = Get_Item_DbContext),但后来我得到了另一个错误...无法访问已释放的上下文实例。
更新 我尝试将上下文注入 DalBase。单击运行第二个操作时仍然出现错误。在这种情况下,可能是因为这两个操作都通过同一个 VM 运行,该 VM 现在具有一个 _dataAccess 和一个注入的上下文。我试图通过从每个函数中获取一个新实例来避免这种情况。
所以我的问题是这些 如何找出哪些线程(及其调用堆栈)正在获得相同的上下文? 为什么两个单独的线程(来自异步调用)从 GetService 获取相同的上下文? 是否有我必须添加到 AddDbContext 以使其范围正确的选项?
谢谢 迈克
【问题讨论】:
-
您是否可能在没有先将查询结果复制到 BindingList 的情况下对查询结果进行数据绑定?还是启用了延迟加载?
-
如果有一个列表被返回,那么我使用
Task<List<Item>>而不是Task<IEnumerable<Item>>和await db.Items.ToListAsync()如果这就是你的意思。我不确定延迟加载。如果有记忆,默认情况下不会启用。我必须专门启用它,对吗? -
如果您的导航属性是虚拟的,则在 EF6 中默认启用延迟加载。 docs.microsoft.com/en-us/ef/ef6/querying/…
-
感谢您的快速回复。我会通读一遍,看看是否可行。
-
同样在 WPF MVVM 中,我认为您应该只看到来自 UI 线程的 DbContext 访问。所以你可以在 EF Logging 中转储 ThreadId。
标签: c# entity-framework mvvm dependency-injection async-await