为什么等待不会出现阻止EF上下文的第二次操作

时间:2016-01-13 17:46:38

标签: c# asp.net-mvc entity-framework asynchronous async-await

在ASP.NET MVC应用程序中,我收到了一个使用我的Entity Framework上下文的控制器方法的以下错误消息。

  

在上一次异步操作完成之前,在此上下文中启动了第二个操作。使用'await'确保在此上下文上调用另一个方法之前已完成任何异步操作。不保证任何实例成员都是线程安全的。

我知道你不能并行运行查询,所有内容似乎都在等待。如果我调试程序并逐步检查从EF返回的一些数据,那么它可以工作,可能是因为这会强制查询完成。

编辑如果我在控制器方法的空检查处放置断点并检查shipmentDetail的数据,则不会抛出异常。

以下是代码的信息:

控制器方法

[Route("{id:int}/Deliveries")]
public async Task<ActionResult> DeliveryInfo(int id)
{
    var shipmentDetail = await db.ShipmentDetails.SingleOrDefaultAsync(s => s.Id == id);
    if (shipmentDetail == null)
        return HttpNotFound(string.Format("No shipment detail found with id {0}", id));
     var model = await DeliveryInfoModel.CreateModel(db, shipmentDetail);
    return View("DeliveryInfo", model);
}

CreateModel方法

public static async Task<DeliveryInfoModel> CreateModel(Context db, ShipmentDetail shipment)
{
    DeliveryInfoModel model = new DeliveryInfoModel()
    {
        ShipmentInfo = shipment
    };

    //initialize processing dictionary
    Dictionary<int, bool> boxesProcessed = new Dictionary<int, bool>();
    List<DeliveryBoxStatus> statuses = new List<DeliveryBoxStatus>();

     for (int i = 1; i <= shipment.BoxCount; i++ )
        {
            boxesProcessed.Add(i, false);
        }

        //work backwards through process

        //check for dispositions from this shipment
        if(shipment.Dispositions.Count > 0)
        {
             foreach (var d in shipment.Dispositions)
            {
                DeliveryBoxStatus status = new DeliveryBoxStatus()
                {
                    BoxNumber = d.BoxNumber,
                    LastUpdated = d.Date,
                    Status = d.Type.GetDescription().ToUpper()
                };

                statuses.Add(status);
                boxesProcessed[d.BoxNumber] = true;
            }
        }

        //return if all boxes have been accounted for
        if (boxesProcessed.Count(kv => kv.Value) == shipment.BoxCount)
        {
            model.BoxStatuses = statuses;
            return model;
        }

        //check for deliveries
        if(shipment.Job_Detail.Count > 0)
        {
            foreach (var j in shipment.Job_Detail.SelectMany(d => d.DeliveryInfos))
            {
                DeliveryBoxStatus status = new DeliveryBoxStatus()
                {  
                    BoxNumber = j.BoxNumber,
                    LastUpdated = j.Job_Detail.To_Client.GetValueOrDefault(),
                    Status = "DELIVERED"
                };

                statuses.Add(status);
                boxesProcessed[j.BoxNumber] = true;
            }
        }

    //check for items still in processing & where
    foreach (int boxNum in boxesProcessed.Where(kv => !kv.Value).Select(kv => kv.Key))
    {
       //THIS LINE THROWS THE EXCEPTION
        var processInfo = await db.Processes.Where(p => p.Jobs__.Equals(shipment.Job.Job__, StringComparison.InvariantCultureIgnoreCase) && p.Shipment == shipment.ShipmentNum && p.Box == boxNum)
                                .OrderByDescending(p => p.date)
                                .FirstOrDefaultAsync();

       //process returned data
       //...
    }

    model.BoxStatuses = statuses;

    return model;
}

我不完全确定这是因为在控制器中进行的查询,还是因为在循环中进行的查询未完成导致此行为。由于EF的懒惰,或者async / await在这种情况下如何工作,是否有什么我不理解何时实际制作/返回查询?我有很多其他的方法和进行异步EF调用并且之前没有遇到此问题的控制器。

修改

我的上下文使用Ninject作为我的IoC容器注入我的控制器。这是NinjectWebCommon的RegisterServices方法中的配置:

kernel.Bind<Context>().ToSelf().InRequestScope();

1 个答案:

答案 0 :(得分:7)

在与Entity Framework使用异步时避免延迟加载。相反,要么首先加载您需要的数据,要么使用sudo update-grub 来确保您需要的数据随查询一起加载。

https://msdn.microsoft.com/en-gb/magazine/dn802603.aspx

  

异步支持的当前状态

     

......异步   支持被添加到Entity Framework(在EntityFramework NuGet中)   在版本6中包。)你必须小心避免懒惰   但是,在异步工作时加载,因为延迟加载是   总是同步执行。 ...

(强调我的)

此外:

https://entityframework.codeplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#ThreadSafety