实体框架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 (&gt;1000) - 你能用那个错误的细节更新你的问题吗(包括异常类型和消息以及内部异常细节,如果有的话)。 @Igor- ,请确保我的问题已更新,其中包含有关错误的详细信息。 @Ivan - 我更新了我的答案。它是由竞争条件引起的,通过启动多个都调用 ReloadAsync 的任务。您应该使用我刚刚发布的代码同步调用它们。 @Igor- 谢谢。我已经测试了您的最新建议没有错误,并认为解决了问题。

以上是关于实体框架6、加载实体async/await的主要内容,如果未能解决你的问题,请参考以下文章

为啥实体框架 6 中仍未实现 ON DELETE SET NULL?有障碍吗?

使用急切加载时,实体框架是不是将所有相关实体加载到无限深度

为啥实体框架核心加载实体的关系而不添加包含

从实体框架中的集合加载相关实体

实体框架 - 停止按需延迟加载相关实体?

实体框架 CTP5 重新加载相关实体