EF Core - 在上一个操作完成之前,在此上下文中启动了第二个操作。不保证任何实例成员都是线程安全的

Kgn*_*web 6 c# entity-framework-core asp.net-core ef-core-2.1

我正在使用EF Core 2.1并且我正在尝试使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

但这会引发以下错误。

在前一个操作完成之前,在此上下文中启动了第二个操作。不保证任何实例成员都是线程安全的。

启动文件

services.AddScoped<IMyRepository, MyRepository>();
Run Code Online (Sandbox Code Playgroud)

我该如何解决这个问题?

Tse*_*eng 7

你不能那样做。EF Core(与之前的 EF 6 一样)不是线程安全的。

您必须等待一项任务才能开始下一项任务

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);
Run Code Online (Sandbox Code Playgroud)

在这里,您正在并行启动多个任务并等待它们。你必须循环它

foreach(var entity in events) 
{
    Context.Entry(entity).Property(x => x.Attendance).IsModified = true;
});
await Context.SaveChangesAsync();
Run Code Online (Sandbox Code Playgroud)

它不太确定为什么要逐个事件地保存它(因此,该示例更改为在调用之前执行所有修改的标志SaveChangesAsync()),因为它的效率相当低。更新所有属性,然后最后运行 SaveChanges 更有意义,因为 EF Core 将在您保存更改时保存所有更改/跟踪的实体。当出现问题时也更容易进行回滚(SaveChangesAsync 中的操作发生在事务范围内)