为什么等待不会出现prevent第二操作上EF上下文 [英] Why Does Await Not Appear to Prevent Second Operation on EF Context

查看:179
本文介绍了为什么等待不会出现prevent第二操作上EF上下文的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在ASP.NET MVC应用程序,我recieving对于使用我的实体框架上下文的我的控制器的方法之一以下错误消息。


  

开始在此背景下的第二个操作的previous异步操作完成之前。使用等待,以确保调用此背景下的另一种方法之前,任何异步操作已经完成。任何实例成员不能保证是线程安全的。


我知道,你不能并行运行的查询,并且一切似乎都正确地等待。如果我调试程序和步骤,并检查了一些数据从EF返回然后它工作,可能是因为这会强制查询完成。

修改如果我把一个断点在控制器方法的空检查,检查的数据 shipmentDetail 的没有抛出异常。

这里的code的SNIPPIT:

控制器方法

  [路线({ID:INT} /交付)]
公共异步任务<&的ActionResult GT; DeliveryInfo(INT ID)
{
    VAR shipmentDetail =等待db.ShipmentDetails.SingleOrDefaultAsync(S = GT; s.Id == ID);
    如果(shipmentDetail == NULL)
        返回HttpNotFound(的String.Format(找不到ID找到出货明细{0},ID));
     VAR模型=等待DeliveryInfoModel.CreateModel(DB,shipmentDetail);
    返回视图(DeliveryInfo模型);
}

CreateModel法

 公共静态异步任务< D​​eliveryInfoModel> CreateModel(上下文分贝,ShipmentDetail出货)
{
    DeliveryInfoModel模式=新DeliveryInfoModel()
    {
        ShipmentInfo =出货
    };    //初始化处理字典
    字典< INT,BOOL> boxesProcessed =新词典< INT,BOOL>();
    清单< D​​eliveryBoxStatus>状态=新的List< D​​eliveryBoxStatus>();     的for(int i = 1; I< = shipment.BoxCount;我++)
        {
            boxesProcessed.Add(I,FALSE);
        }        //通过流程向后工作        //检查来自这批货意向
        如果(shipment.Dispositions.Count大于0)
        {
             的foreach(在shipment.Dispositions变种D)
            {
                DeliveryBoxStatus状态=新DeliveryBoxStatus()
                {
                    BoxNumber = d.BoxNumber,
                    LASTUPDATED = d.Date,
                    状态= d.Type.GetDescription()。ToUpper的()
                };                statuses.Add(状态);
                boxesProcessed [d.BoxNumber] = TRUE;
            }
        }        //返回如果所有的箱子已占到
        如果(boxesProcessed.Count(KV => kv.Value)== shipment.BoxCount)
        {
            model.BoxStatuses =状态;
            回归模型;
        }        //检查交付
        如果(shipment.Job_Detail.Count大于0)
        {
            的foreach(在shipment.Job_Detail.SelectMany(D变种J => d.DeliveryInfos))
            {
                DeliveryBoxStatus状态=新DeliveryBoxStatus()
                {
                    BoxNumber = j.BoxNumber,
                    LASTUPDATED = j.Job_Detail.To_Client.GetValueOrDefault(),
                    状态=交付
                };                statuses.Add(状态);
                boxesProcessed [j.BoxNumber] = TRUE;
            }
        }    //检查仍在处理及放物品;哪里
    的foreach(在boxesProcessed.Where诠释boxNum(KV =>!kv.Value)。选择(KV => kv.Key))
    {
       //此行抛出异常
        变种processInfo =等待db.Processes.Where(p值=> p.Jobs __的Equals(shipment.Job.Job__,StringComparison.InvariantCultureIgnoreCase)及&放大器; p.Shipment == shipment.ShipmentNum&放大器;&放大器; p.Box = = boxNum)
                                .OrderByDescending(P => p.date)
                                .FirstOrDefaultAsync();       //处理返回的数据
       // ...
    }    model.BoxStatuses =状态;    回归模型;
}

我不能完全肯定,如果这是因为在控制器中进行的查询,或者因为未完成造成这种现象的循环作出查询。有什么事我不明白什么时候该查询实际是由于EF的懒惰做/返回,或者如何异步/等待在这种情况下工作?我有很多其他的方法和放大器;控制器,使异步调用EF并没有碰到这个previously。

修改

我的情况下被注入到使用Ninject作为我IoC容器我的控制器。下面是它的NinjectWebCommon的RegisterServices方法内部配置:

  kernel.Bind<语境方式>()ToSelf()InRequestScope();


解决方案

使用异步实体框架时,避免延迟加载。相反,无论是加载你需要先将数据,或使用包括()的保证你需要的是加载查询数据。

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


  

异步支持当前状态


  
  

...异步
  支持加入到实体框架(在的EntityFramework的NuGet
  封装)在第6版本的你必须要小心,避免偷懒
  异步加载工作的时候,不过,因为懒加载
  总是同步执行
的。 ...


(重点煤矿)

还有:

<一个href=\"https://entityframework.$c$cplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#ThreadSafety\" rel=\"nofollow\">https://entityframework.$c$cplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#ThreadSafety

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.

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.

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.

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.

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:

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.

EDIT

My context is injected into my controller using Ninject as my IoC container. Here's its config inside of NinjectWebCommon's RegisterServices method:

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

解决方案

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.

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. ...

(Emphasis mine)

Also:

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

这篇关于为什么等待不会出现prevent第二操作上EF上下文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆