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?
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
}