为什么等待似乎没有阻止EF上下文上的第二次操作

时间:2022-01-15 02:14:21

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. Any instance members are not guaranteed to be thread safe.

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

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? 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. Here's its config inside of NinjectWebCommon's RegisterServices method:

我的上下文被注入到我的控制器中,使用Ninject作为我的IoC容器。这是它的配置在忍者webcommon的RegisterServices方法:

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

1 个解决方案

#1


6  

Avoid lazy loading when using async with 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/en-gb/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中,异步支持被添加到实体框架(在EntityFramework NuGet包中)。但是,在异步地工作时,您必须小心避免延迟加载,因为延迟加载总是同步执行的。

(Emphasis mine)

(强调我的)

Also:

另外:

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

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

#1


6  

Avoid lazy loading when using async with 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/en-gb/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中,异步支持被添加到实体框架(在EntityFramework NuGet包中)。但是,在异步地工作时,您必须小心避免延迟加载,因为延迟加载总是同步执行的。

(Emphasis mine)

(强调我的)

Also:

另外:

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

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