简体   繁体   English

为什么未出现以阻止在EF上下文中进行第二次操作

[英]Why Does Await Not Appear to Prevent Second Operation on EF Context

Within an ASP.NET MVC Application I'm recieving the following error message for one of my controller methods that uses my Entity Framework context. 在ASP.NET MVC应用程序中,对于使用我的实体框架上下文的一种控制器方法,我收到以下错误消息。

A second operation started on this context before a previous asynchronous operation completed. 在先前的异步操作完成之前,第二操作在此上下文上开始。 Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. 使用“ await”来确保在此上下文上调用另一个方法之前,所有异步操作都已完成。 Any instance members are not guaranteed to be thread safe. 不保证任何实例成员都是线程安全的。

I'm aware that you cannot run queries in parallel, and everything appears to be awaited properly. 我知道您不能并行运行查询,并且一切似乎都已正确等待。 If I debug the program and step and inspect some of the data returned from EF then it works, probably because this forces the queries to complete. 如果我调试程序并逐步检查从EF返回的一些数据,则它可以工作,可能是因为这迫使查询完成。

EDIT If I place a breakpoint at the null check in the controller method and inspect the data of shipmentDetail the exception is NOT thrown. 编辑如果我把一个断点在控制器方法空校验和检查的数据shipmentDetail的没有抛出异常。

Here's a snippit of the code: 这是代码片段:

Controller Method : 控制器方式

[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 Method : 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;
}

I'm not completely sure if it's because of the query made in the controller, or because of the queries made in the loop that aren't completing causing this behavior. 我不确定是由于控制器中的查询还是由于循环中的查询未完成导致此行为。 Is there something I'm not understanding about when the queries are actually made/returned due to EF's laziness, or how async/await works in this situation? 由于EF的懒惰,何时真正执行/返回查询,或者在这种情况下异步/等待如何工作,我是否不了解? I have a lot of other methods & controllers that make async EF calls and haven't run into this previously. 我还有很多其他方法和控制器可以进行异步EF调用,并且以前没有遇到过这种情况。

EDIT 编辑

My context is injected into my controller using Ninject as my IoC container. 我的上下文是使用Ninject作为我的IoC容器注入到我的控制器中的。 Here's its config inside of NinjectWebCommon's RegisterServices method: 这是NinjectWebCommon的RegisterServices方法内部的配置:

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

Avoid lazy loading when using async with Entity Framework. 与Entity Framework异步使用时,请避免延迟加载。 Instead, either load the data you need first, or use Include() 's to ensure the data you need is loaded with the query. 取而代之的是,要么先加载您需要的数据,要么使用Include()来确保查询中加载了您需要的数据。

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

Current State of Async Support 异步支持的当前状态

... Async support was added to Entity Framework (in the EntityFramework NuGet package) in version 6. You do have to be careful to avoid lazy loading when working asynchronously, though, because lazy loading is always performed synchronously . ...异步支持已在版本6中添加到了Entity Framework(在EntityFramework NuGet包中)。 但是,由于异步加载始终是同步执行的,因此必须小心避免延迟加载 ... ...

(Emphasis mine) (强调我的)

Also: 也:

https://entityframework.codeplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#ThreadSafety https://entityframework.codeplex.com/wikipage?title=基于任务的%20Asynchronous%20Pattern%20support%20in%20EF。#ThreadSafety

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么在已经使用await时要求我使用它(在此上下文中启动了第二个操作)? - Why am I asked to use await when I am already doing so (A second operation started on this context)? 在这种情况下,如何防止在上下文中进行第二次操作? - How to prevent second operation on context in this case? await在异步操作后没有恢复上下文? - await does not resume context after async operation? 在此上下文中启动了第二个操作,Domain Event EF Core 3.1 - A second operation started on this context, Domain Event EF Core 3.1 在UserManager上等待导致错误在上一个操作完成之前,在此上下文中启动了第二个操作 - await on UserManager causes the error A second operation started on this context before a previous operation completed EF 错误:在前一个异步操作完成之前在此上下文上启动了第二个操作 - EF error: A second operation started on this context before a previous asynchronous operation completed 在前一个操作完成之前,在此上下文上启动了第二个操作 Asp.net EF Core - A second operation started on this context before a previous operation completed Asp.net EF Core EF Core“在上一个操作完成之前在此上下文中启动了第二个操作”仅在部署时 - EF Core "A second operation started on this context before a previous operation completed" only when deployed Blazor 服务器和 EF Core:在上一个操作完成之前在此上下文实例上启动了第二个操作 - Blazor Server and EF Core: A second operation was started on this context instance before a previous operation completed 在此上下文中启动了第二个操作 - A second operation started on this context
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM