【问题标题】:Linq Call - There is already an open DataReader associated with this Command which must be closed firstLinq 调用 - 已经有一个打开的 DataReader 与此命令关联,必须先关闭
【发布时间】:2015-07-24 14:00:30
【问题描述】:

我有一些代码在运行时会引发“EntityCommandExecutionException”类型的异常。

Visual Studio 指向的线:

else if (item.FirstOrDefault().InspectionEquipmentTypes.Any())

异常的内部细节说:

There is already an open DataReader associated with this Command which must be closed first.

我的问题是引发错误的那一行没有尝试使用数据库/数据读取器(据我所知),所以我不确定为什么会生成此异常。

编辑:

    public static IEnumerable<IGrouping<string,Entities.Inspection>> GetUnscheduledBatchInspections(Entities.EntityModel context)
    {
        var results = context.Inspections.Where(w =>
            w.InspectionBatchNo != null
            && w.IsCancelled == false
            && !w.CalendarItems.Any()
            && w.Duration.HasValue).GroupBy(g => g.InspectionBatchNo);
        return results;
    }

调用方法:

    private void MapBatchInspectionsToViewModel(ref SchedulerViewModel viewModel)
    {
        var batchInspections = SchedulerManager.GetUnscheduledBatchInspections(this.Context);

        foreach (var item in batchInspections)
        {
            var bigi = new BatchInspectionGridItem();
            if (item.Any())
            {
                bigi.BatchInspectionNo = item.First().InspectionBatchNo;

                if (item.FirstOrDefault().EquipmentTypeID != null)
                {
                    bigi.EquipmentTypeName = item.FirstOrDefault().EquipmentType.Description;
                }
                else if (item.FirstOrDefault().InspectionEquipmentTypes.Any())
                {
                    bigi.EquipmentTypeName = string.Join(" / ", item.FirstOrDefault().InspectionEquipmentTypes.Select(s => s.EquipmentType.Description));
                }
                bigi.CustomerName = item.First().CustomerSite.Customer.CustomerName;
                bigi.CustomerID = item.First().CustomerSite.Customer.CustomerID;
                bigi.NumberOfInspections = item.Count();
                bigi.TotalDuration = item.Sum(s => s.Duration);
            }

            viewModel.BatchInspectionGridViewModel.Add(bigi);
        }
    }

【问题讨论】:

  • 你能再贴一些代码来说明item是如何创建的吗?
  • 您在条件表达式中使用 LINQ。可以想象,以前的条件表达式将上下文绑定以进行类似的检查。
  • 我猜你正在使用 linq to sql 或 linq to entity 框架。无论哪种方式,您都可以设置与 dbase 的连接以允许多个活动结果集,从而使您能够同时使用多个 DataReader。只需将以下内容添加到您的连接字符串中:MultipleActiveResultSets=true;

标签: c# linq


【解决方案1】:

会发生以下情况:当您循环访问batchInspections 时,数据库读取器正在从数据库中读取此集合。在循环中,您通过大量的First(OrDefault) 调用、SumCount 进行新的数据库读取。这会导致异常“已经有一个打开的 DataReader...”。

正如 George Lica 所说,您可以通过在连接字符串中设置 MultipleActiveResultSets=True 来解决此问题。

或者您可以在循环开始迭代之前完成阅读batchInspections...

foreach (var item in batchInspections.ToList())

但是,首先收集您需要的数据并然后遍历它们会更有效:

foreach (var item in batchInspections
            .Select(b => new 
                         {
                             First = b.FirstOrDefault(),
                             Count = b.Count(),
                             Sum = b.Sum(s => s.Duration)
                         } )
            .ToList())
{
    var bigi = new BatchInspectionGridItem();
    if (item.Any())
    {
        bigi.BatchInspectionNo = item.First.InspectionBatchNo;

        if (item.First.EquipmentTypeID != null)
        {
            bigi.EquipmentTypeName = item.First.EquipmentType.Description;
        }
        else if (item.First.InspectionEquipmentTypes.Any())
        {
            bigi.EquipmentTypeName = string.Join(" / ", item.First.InspectionEquipmentTypes.Select(s => s.EquipmentType.Description));
        }
        bigi.CustomerName = item.First.CustomerSite.Customer.CustomerName;
        bigi.CustomerID = item.First.CustomerSite.Customer.CustomerID;
        bigi.NumberOfInspections = item.Count;
        bigi.TotalDuration = item.Sum;
    }

    viewModel.BatchInspectionGridViewModel.Add(bigi);
}

我希望SchedulerManager.GetUnscheduledBatchInspections返回一个IQueryable,这样后面的Select变成匿名类型就会被翻译成SQL。

必须说,虽然使用 Entity Framework 激活 MARS 几乎总是一个好主意,因为延迟加载有一种导致此异常的方法。

【讨论】:

    【解决方案2】:

    当您以嵌套方式进行查询时会发生这种情况。

    item.FirstOrDefault().InspectionEquipmentTypes.ToList().Any()
    

    可能会起作用。不过我不确定。尝试简化嵌套查询。例如,不要进行如下查询:

    items.Where(/*some condition*/).Any();
    

    改为制作

    items.Any(/*some condition*/);
    

    【讨论】:

    • 你能解释一下为什么 items.Where("condition").Any();比 items.Any("condition") 更糟糕?我认为 .Any() 只是检查结果集而不运行额外的查询?
    • 虽然肯定有技术上的好处,但我并不完全承认它们,但增加可读性就足够了。
    【解决方案3】:

    如果你真的想要嵌套查询(我不建议这样做,我宁愿使用一些散列数据结构进行单独的查询和链接实体)并且你正在使用 sql server,你实际上有一个替代方案:激活 MARS。要激活它,只需添加连接字符串 MultipleActiveResultSets=True。更多详情请点击此链接:https://msdn.microsoft.com/en-us/library/h32h3abf(v=vs.110).aspx

    【讨论】:

      猜你喜欢
      • 2011-08-29
      • 1970-01-01
      • 1970-01-01
      • 2012-02-19
      • 1970-01-01
      • 2017-07-15
      相关资源
      最近更新 更多