Entity framework 6, load entities async/await

async-await c# entity-framework-6

Question

I want to reload certain entities from db, due to the long operation I want to reload them using async/await. Here is my code:

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);
    }

I get the error:

A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.**

Exception details:

{System.NotSupportedException: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe. at System.Data.Entity.Internal.ThrowingMonitor.EnsureNotEntered() at System.Data.Entity.Core.Objects.ObjectContext.RefreshAsync(RefreshMode refreshMode, Object entity, CancellationToken cancellationToken) at System.Data.Entity.Internal.InternalEntityEntry.ReloadAsync(CancellationToken cancellationToken) at System.Data.Entity.Infrastructure.DbEntityEntry`1.ReloadAsync() at PlanningModule.PlanningForm.d__20.MoveNext() in C:\Git\planningmodule\PlanningModule\PlanningForm.cs:line 307 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at PlanningModule.PlanningForm.d__19.MoveNext() in C:\Git\planningmodule\PlanningModule\PlanningForm.cs:line 264}

Inner exception:

null

Stack trace:

_stackTrace {sbyte[192]} object {sbyte[]}

Anys ideas, how to reload entities async?

1
1
5/12/2016 9:21:03 AM

Accepted Answer

You have to materialize the list before you can perform additional actions on that DbContext as the DbContext is being actively used when you enumerate an IQueryable. So adding .ToList() would solve the issue as all entities are then retrieved from the DbContext before the iteration starts.

foreach (Entities.Apointment app in apps.ToList()) // added ToList()

If you wanted to materialize the list using async you could do

foreach (Entities.Apointment app in (await apps.ToListAsync()))

Edit from comments

I should note that the error I get is often related to many entities being reloaded (>1000)

The only other thing I can think of is that there is a race condition in await Task.WhenAll(task); where the tasks (you defined in the loop previous to this statement) are simultaneously calling the DbContext. Without additional information I cannot tell you for sure though (let me know if its the exact same error or a different one). In that case you should execute each call synchronously inside the loop which can still be done using the await/async call.

// 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
}
2
5/12/2016 9:16:17 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow