代码之家  ›  专栏  ›  技术社区  ›  Sean

EF6:在异步任务中使用dbContext保存到同一实体

  •  0
  • Sean  · 技术社区  · 6 年前

    我从数据库加载对象/实体:

    var db = new DbContext();
    
    //...
    
    var item = db.Items.First();
    

    然后,我要执行两个异步任务,即当返回时,更新项目的数据:

    var task1 = Function1(db, item); 
    var task2 = Function2(db, item); 
    await Task.WhenAll(new Task[] { task1 , task2 });
    

    这两个函数将具有一些代码,用于获取、设置和保存项的(不同)属性,如:

    var orderId = await CallApi();
    item.OrderId = orderId;
    db.Entry(item).State = EntityState.Modified;
    await db.SaveChangesAsync();
    

    但是,由于它们是异步运行的,所以我得到了错误: A second operation started on this context before a previous asynchronous operation completed

    我尝试在函数中更新dbContext,但得到了错误 An entity object cannot be referenced by multiple instances of IEntityChangeTracker

    我理解为什么我会得到这两个错误,我的问题是:什么编码模式最能解决这个问题?


    编辑 好的,所以上面是一个简单的例子。实际上,函数中还有很多工作要做,因此很难将所有的逻辑从函数中移出并移入调用方法中。我也希望所有的工作都是自给自足的。

    上的属性 item 那个 Function1 Function2 更新是离散的。我在用 this library 以确保保存不会覆盖所有字段。

    2 回复  |  直到 5 年前
        1
  •  1
  •   demo    5 年前

    你可以移动代码

    db.Entry(item).State = EntityState.Modified; 
    await db.SaveChangesAsync();
    

    Task.Whenall() . 这两个函数的任务是更新该对象的属性。

        2
  •  1
  •   Harald Coppoolse    6 年前

    不要让两个线程在同一个dbContext上执行操作。dbContext跟踪获取的项。如果两个线程同时获取和更改同一dbContext上的项,则可能会导致不需要的结果。

    另外:如果你得到一个 Item ,只需更改所需的属性。您不需要将状态设置为Modified。dbContext可以检查它是否具有原始值。

    因此,创建创建自己的dbContext、获取请求的数据、更改数据、保存数据并最终释放dbContext的函数。

    async Task Function1(...)
    {
        using (var dbcontext = new MyDbContext(...))
        {
            // Start Fetching the item that must be changed; don't await yet
            var taskFetchItemToChange = dbContext.Items
                .Where(...)
                .FirstOrDefaultAsync();
    
            // Start fetching the orderId that must be changed; don't await yet
            var taskFetchOrderId = this.CallApiAsync();
    
            // await until both item and orderId are fetched:
            await Task.WhenAll(new Task[] {taskFetchItemToChange, taskFetchOrderId});
    
            var fetchedItemToChange = taskFetchItemToChange.Result;
            var fetchedOrderId = taskFetchOrderId.Result;
            fetchedItemToChange.OrderId = fetchedOrderId;
            // or do this in one big unreadable unmaintainable untestable step
    
            await dbContext.SaveChangesAsync();
        }
    }
    

    您将有一个类似的函数2,或者可能是具有不同参数的相同函数1。

    var taskFunction1 = Function1();
    var taskFunction2 = Function2();
    await Task.WhenAll( new Task[] {taskFunction1, taskFunction2});