实体框架6、加载实体async/await
Posted
技术标签:
【中文标题】实体框架6、加载实体async/await【英文标题】:Entity framework 6, load entities async/await 【发布时间】:2016-09-07 20:05:21 【问题描述】:我想从 db 重新加载某些实体,因为我想使用 async/await 重新加载它们的长时间操作。这是我的代码:
private async void btnUpdate_Click(object sender, EventArgs e)
Task allTasks = UpdateAppointments();
await allTasks;
internal async Task UpdateAppointments()
IEnumerable<Entities.Apointment> apps = PContext.Appointments.Select(p => p).Where(p => p.Start >= Start && p.End <= End && p.MachineId == 66);
List<Task> task = new List<Task>();
foreach (Entities.Apointment app in apps)
task.Add(PContext.Entry(app).ReloadAsync());
await Task.WhenAll(task);
我得到错误:
在前一个异步操作完成之前,在此上下文上启动了第二个操作。使用“等待”确保任何 异步操作在调用另一个方法之前已经完成 在这种情况下。不保证任何实例成员都是线程 安全。**
异常详情:
System.NotSupportedException:在前一个异步操作完成之前,在此上下文上启动了第二个操作。使用 'await' 确保在此上下文中调用另一个方法之前所有异步操作都已完成。不保证任何实例成员都是线程安全的。 在 System.Data.Entity.Internal.ThrowingMonitor.EnsureNotEntered() 在 System.Data.Entity.Core.Objects.ObjectContext.RefreshAsync(刷新模式刷新模式,对象实体,CancellationToken 取消令牌) 在 System.Data.Entity.Internal.InternalEntityEntry.ReloadAsync(CancellationToken cancelToken) 在 System.Data.Entity.Infrastructure.DbEntityEntry`1.ReloadAsync() 在 C:\Git\planningmodule\PlanningModule\PlanningForm.cs:line 307 中的 PlanningModule.PlanningForm.d__20.MoveNext() --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务) 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 System.Runtime.CompilerServices.TaskAwaiter.GetResult() 在 C:\Git\planningmodule\PlanningModule\PlanningForm.cs:line 264 中的 PlanningModule.PlanningForm.d__19.MoveNext()
内部异常:
空
堆栈跟踪:
_stackTrace sbyte[192] 对象 sbyte[]
任何想法,如何异步重新加载实体?
【问题讨论】:
【参考方案1】:您必须先实现该列表,然后才能对该 DbContext
执行其他操作,因为在您枚举 IQueryable
时正在积极使用 DbContext
。因此,添加.ToList()
将解决问题,因为在迭代开始之前,所有实体都会从DbContext
中检索出来。
foreach (Entities.Apointment app in apps.ToList()) // added ToList()
如果您想使用 async
实现列表,您可以这样做
foreach (Entities.Apointment app in (await apps.ToListAsync()))
编辑 来自 cmets
我应该注意,我得到的错误通常与许多正在重新加载的实体有关 (>1000)
我能想到的唯一另一件事是await Task.WhenAll(task);
中存在竞争条件,其中任务(您在此语句之前的循环中定义)同时调用DbContext
.如果没有其他信息,我不能肯定地告诉你(让我知道它是完全相同的错误还是不同的错误)。在这种情况下,您应该在循环内同步执行每个调用,这仍然可以使用 await/async 调用来完成。
// note, for readability I changed the explicit types to keyword var to make the code a little more compact and hopefully easier identify the changes
var apps = await PContext.Appointments.Select(p => p).Where(p => p.Start >= Start && p.End <= End && p.MachineId == 66).ToListAsync(); // Retrieve all instances on the context in one call
foreach (var app in apps)
await PContext.Entry(app).ReloadAsync(); // execute each Reload synchronously instead of concurrently
【讨论】:
感谢您的建议。ToList() 不起作用。 Await apps.ToListAsync() 有时会工作,有时会产生与以前相同的错误。我应该注意,我得到的错误通常与许多正在重新加载的实体有关 (>1000) @Ivan -I should note that the error I get is often related to many entities being reloaded (>1000)
- 你能用那个错误的细节更新你的问题吗(包括异常类型和消息以及内部异常细节,如果有的话)。
@Igor- ,请确保我的问题已更新,其中包含有关错误的详细信息。
@Ivan - 我更新了我的答案。它是由竞争条件引起的,通过启动多个都调用 ReloadAsync 的任务。您应该使用我刚刚发布的代码同步调用它们。
@Igor- 谢谢。我已经测试了您的最新建议没有错误,并认为解决了问题。以上是关于实体框架6、加载实体async/await的主要内容,如果未能解决你的问题,请参考以下文章