【问题标题】:Why Does Await Not Appear to Prevent Second Operation on EF Context为什么等待似乎不会阻止对 EF 上下文的第二次操作
【发布时间】:2016-01-14 05:26:44
【问题描述】:

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

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

我知道您不能并行运行查询,而且一切似乎都在正常等待。如果我调试程序并单步检查从 EF 返回的一些数据,那么它可以工作,可能是因为这会强制查询完成。

编辑如果我在控制器方法中的 null 检查处放置断点并检查 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();

【问题讨论】:

  • 我怀疑你的错误来自其他地方,这段代码应该没问题。 A) 您的db 是否与其他视图/视图模型共享? B) 你有没有用调试器单步调试它,看看它是否仍然抛出?
  • A) 不,它没有被共享,它被注入到控制器并且只在那里使用,这些是使用 db 上下文的仅有的 2 个操作用于此 http 请求。 B)如果我检查从 EF 返回的数据(我在问题中提到过),则使用调试器不会抛出,否则会抛出。
  • 你能详细说明它是如何注入的吗?它是从 IoC 容器注入的吗?如果是这样,您的 IoC 注册是如何配置的?
  • 我使用 Ninject 作为我的 IoC 容器。我会将上下文的配置添加到我的问题中
  • 您的db 变量是如何声明的?你可能做到了static

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


【解决方案1】:

在实体框架中使用异步时避免延迟加载。相反,要么先加载您需要的数据,要么使用Include()'s 来确保您需要的数据与查询一起加载。

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

异步支持的当前状态

...异步 支持已添加到实体框架(在 EntityFramework NuGet 包)在第 6 版中。你必须小心避免懒惰 但是,在异步工作时加载,因为延迟加载是 始终同步执行。 ...

(强调我的)

还有:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-27
    • 1970-01-01
    • 2021-06-18
    • 1970-01-01
    • 1970-01-01
    • 2019-04-19
    • 1970-01-01
    • 2013-01-27
    相关资源
    最近更新 更多