在前一个异步操作完成之前,在此上下文上启动了第二个操作。使用“等待”来确保
Posted
技术标签:
【中文标题】在前一个异步操作完成之前,在此上下文上启动了第二个操作。使用“等待”来确保【英文标题】:A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure 【发布时间】:2020-01-23 20:04:27 【问题描述】:我在将 EntityFrmework 6.2 异步方法用于 winform 应用程序时遇到问题。 我们使用自定义类来管理基于单个活动设置的同步或异步 EntityFrmework 访问。当我们使用同步访问时,一切正常。当我们使用异步调用时,我们会遇到这个问题:在前一个异步操作完成之前,在此上下文上启动了第二个操作。使用 'await' 确保在此上下文上调用另一个方法之前已完成任何异步操作。不保证任何实例成员都是线程安全的。 我们已经阅读了很多关于这个问题的帖子,但没有一个可以帮助我们解决我们的问题。
我们知道问题与从同步调用异步方法有关,不幸的是我们无法将调用方法从同步转换为异步。 我们从我们的项目中提取了两种方法来展示所描述的行为
public void AddLoadingRequest(Func<Task> fnAsync, Action fnSync, Action bsa)
try
Task task = Task.Run(async () => await DoDataSource(fnAsync, fnSync, bsa));
_lstTask.Add(task);
if (task.IsFaulted)
throw task.Exception;
catch (Exception ex)
MessageBox.Show($"Error ex.Message");
protected async Task DoDataSource(Func<Task> fnAsync, Action fnSync, Action bsa)
try
if (_bLoadingFKTableAsync == true)
await fnAsync();
else
fnSync();
catch (Exception ex)
MessageBox.Show(ex.Message, "Loading Error");
throw;
调用方法:
private void btnTest_Click(object sender, EventArgs e)
NWContext wContext = new NWContext();
Customers cust = wContext.Customers.FirstOrDefault(w => w.CustomerID == "RATTC");
BindingSource bsCustomer = new BindingSource();
BindingSource bsOrders = new BindingSource();
BindingSource bsCustomerDemographics = new BindingSource();
bsCustomer.DataSource = cust;
_bLoadingFKTableAsync = true;
AddLoadingRequest(
() => wContext.Entry(cust).Collection(typeof(Orders).Name).LoadAsync(),
() => wContext.Entry(cust).Collection(typeof(Orders).Name).Load(),
() =>
bsOrders.SuspendBinding();
bsOrders.DataSource = cust.Orders;
bsOrders.ResumeBinding();
);
AddLoadingRequest(
() => wContext.Entry(cust).Collection(typeof(CustomerDemographics).Name).LoadAsync(),
() => wContext.Entry(cust).Collection(typeof(CustomerDemographics).Name).Load(),
() =>
bsCustomerDemographics.SuspendBinding();
bsCustomerDemographics.DataSource = cust.CustomerDemographics;
bsCustomerDemographics.ResumeBinding();
);
我们预计 EntityFramework 将在异步模式下加载对象的两个子集合,但我们遇到错误:在前一个异步操作完成之前在此上下文上启动了第二个操作。使用 'await' 确保在此上下文上调用另一个方法之前已完成任何异步操作。不保证任何实例成员都是线程安全的。
【问题讨论】:
那么问题是什么?您专门对AddLoadingRequest
进行了 2 次调用,这两个调用都使用相同的上下文启动了异步操作。在开始第二个之前,您不会 (a) 等待第一个完成。
我们正在尝试从 AddLoadingRequest 同步方法中调用“DoDataSource”异步方法;我们报告了错误,因为我们无法将调用方法 AddLoadingRequest 从同步更改为异步。问题是:我们如何从现有的同步方法中调用两个或多个异步 EF 请求?
【参考方案1】:
问题是:我们如何从现有的同步方法中调用两个或多个异步 EF 请求?
您需要两个或更多数据库上下文。每个人一次只能有一个请求。
附带说明,我强烈建议您将方法更改为async
。像这样跳过await
是一种“一劳永逸”的方式,它有两个主要问题:
-
您的代码无法知道操作何时完成。现在我可以重新使用这个数据库上下文,因为它已经完成了那个数据库操作吗?知道所有数据库更新都已应用,我可以安全退出应用程序吗?这些问题无法用“一劳永逸”的代码来回答。
您的代码无法知道操作是否成功。使用“一劳永逸”,您的代码必须假设它有效。
【讨论】:
我担心唯一的解决方案是获取多个上下文。非常感谢您快速而详尽的回复。 @FabioBorghi 如果这解决了您的问题,请将答案标记为解决方案。以上是关于在前一个异步操作完成之前,在此上下文上启动了第二个操作。使用“等待”来确保的主要内容,如果未能解决你的问题,请参考以下文章
EF 错误:在前一个异步操作完成之前在此上下文上启动了第二个操作
Blazor:在前一个操作完成之前在此上下文上启动了第二个操作
UserManager 错误“在前一个操作完成之前在此上下文上启动了第二个操作”
Entity Framework Core:在前一个操作完成之前在此上下文上启动了第二个操作