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

时间:2018-07-27 10:25:52

标签: c# asynchronous async-await entity-framework-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

我尝试在Function中更新dbContext,但是随后出现错误An entity object cannot be referenced by multiple instances of IEntityChangeTracker

我了解为什么会同时遇到这两个错误,我的问题是:哪种编码模式可以最好地解决此问题?


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

itemFunction1更新的Function2上的属性是离散的。我正在使用this library来确保保存不会覆盖所有字段。

2 个答案:

答案 0 :(得分:1)

您可以移动代码

db.Entry(item).State = EntityState.Modified; 
await db.SaveChangesAsync();
Task.Whenall()之后的

。这两个函数的工作是更新该对象的属性。

答案 1 :(得分:1)

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

此外:如果您获取了Item,则只需更改所需的属性即可。您无需将状态设置为“已修改”。 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();
    }
}

您将拥有相似的Function2,或者具有不同参数的相同Function1。

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