代码之家  ›  专栏  ›  技术社区  ›  Kgn-web

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

  •  0
  • Kgn-web  · 技术社区  · 7 年前

    EF Core 2.1 我试图通过使用一次更新多个实体 Tasks (第三方物流):

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

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

    Startup.cs

    services.AddScoped<IMyRepository, MyRepository>();
    

    1 回复  |  直到 7 年前
        1
  •  3
  •   Tseng    7 年前

    在开始下一项任务之前等待一项任务

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

    在这里,您将并行启动多个任务并等待它们。你得把它绕过去

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

    它不确定为什么要逐个事件保存它(因此,示例更改为在调用之前执行所有修改的标志) SaveChangesAsync() ),因为它效率很低。更合理的做法是更新所有属性,然后最后运行SaveChanges,因为保存更改时,EF Core将保存所有更改/跟踪的实体。当出现错误时,执行回滚也更容易(savechangesync中的操作发生在事务范围内)

        2
  •  1
  •   Augusto Diaz    7 年前

    使用 IServiceScopeFactory 接口(推荐)

    MyRepository :

    private readonly IServiceScopeFactory serviceScopeFactory;
            public MyRepository(IServiceScopeFactory serviceScopeFactory)
            {
                this.serviceScopeFactory = serviceScopeFactory;
            }
    
            public async Task UpdateAttendance(IEnumerable<MyEvents> events)
            {
                try
                {
                    var tasks = events.Select(async entity =>
                    {
                        await Task.Run(() =>
                        {
                            using (var scope = serviceScopeFactory.CreateScope())
                            {
                                var context = scope.GetRequiredService<YourContextType>();
                                context.Entry(entity).Property(x => x.Attendance).IsModified = true;
                                await Context.SaveChangesAsync();
                            }
                        });
                    });
                    await Task.WhenAll(tasks);
                }
                catch (Exception ex)
                {
                    //You really should not do this, did you have a look to ILogger?
                    //If you only wants to stop when an exception gets thrown you can configure VS to do that, Debug menu -> Windows -> Exception Settings
                    throw new Exception(ex.Message ?? ex.InnerException.Message);
                }
            }
    

    或者将DbContext生存时间(默认范围)更改为trasient

    在里面 Startup.cs :

    services.AddDbContext<YourDbContext>(
        ServiceLifetime.Transient,
        options =>  options.UseSqlServer(Configuration.GetConnectionString("SomeConnectionStringName")));
    
    推荐文章