I am using EF Core 2.1
and I am trying to update multiple entities in one go by using Tasks
(TPL):
public async Task UpdateAttendance(IEnumerable<MyEvents> events)
{
try
{
var tasks = events.Select(async entity =>
{
await Task.Run(() => Context.Entry(entity).Property(x => x.Attendance).IsModified = true);
await Context.SaveChangesAsync();
});
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
throw new Exception(ex.Message ?? ex.InnerException.Message);
}
}
But this throwing the below error.
A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
Startup.cs
services.AddScoped<IMyRepository, MyRepository>();
How do I address this?
You can't do that. EF Core (as was EF 6 before) is not thread-safe.
You have to await a task before starting the next one
var tasks = events.Select(async entity =>
{
await Task.Run(() => Context.Entry(entity).Property(x => x.Attendance).IsModified = true);
await Context.SaveChangesAsync();
});
await Task.WhenAll(tasks);
Here you are starting multiple tasks in parallel and awaiting them. You will have to loop over it
foreach(var entity in events)
{
Context.Entry(entity).Property(x => x.Attendance).IsModified = true;
});
await Context.SaveChangesAsync();
It am not really sure why you want to save it event-by-event (hence, the example changed to do all the modified flags before calling SaveChangesAsync()
), as its pretty inefficient. It makes more sense to update all of the properties, then run SaveChanges in the end, since EF Core will save all changed/tracked entities when you Save the Changes. Also easier to do a roll back when something goes wrong (Operation within SaveChangesAsync happens within a transaction scope)