Entity Framework引起超时错误

5
我正在处理以下的Entity Framework查询。我知道这里有很多内容,但希望足够清晰以便于别人可以发现问题。
var lineItems = from li in Repository.Query<CostingLineItem>()

                let cid = (li.ParentCostingPackage != null) ?
                    li.ParentCostingPackage.ParentCostingEvent.ProposalSection.Proposal.Costing.Id :
                    li.ParentCostingEvent.ProposalSection.Proposal.Costing.Id

                where cid == costingId &&
                    li.OriginalProductId.HasValue &&
                    (li.Quantity.HasValue && li.Quantity.Value > 0) &&  // li.QuantityUnitMultiplier
                    Classifications.Contains(li.OriginalProduct.ClassificationEnumIndex)

                let selectedChoiceId = li.OriginalPackageOptionId.HasValue ?
                    (from c in li.OriginalPackageOption.CostingLineItems
                        orderby (c.IsIncluded ?? false) ? -2 : (c.IsDefaultItem ?? false) ? -1 : c.Id
                        select (int)c.OriginalPackageOptionChoiceId).FirstOrDefault() :
                    0

                where selectedChoiceId == 0 || (li.OriginalPackageOptionChoiceId.HasValue && li.OriginalPackageOptionId.Value == selectedChoiceId)

                let hasProviderAvailable = li.OriginalProductItem.ProductItemVendors.Any(
                    piv => piv.ProductPricings.Any(pp => pp.ProductItemVendor.CompanyId != null || pp.ProductItemVendor.HotelId != null))

                select new
                {
                    LineItem = li,
                    ProductItem = li.OriginalProductItem,
                    Product = li.OriginalProduct,
                    Vendors = li.CostingLineItemVendors,
                    HasProviderAvailable = hasProviderAvailable
                };

原始查询会生成以下运行时错误:

等待操作超时

如果我将声明 selectedChoiceId 的部分更改为以下内容,则可以消除该错误:

let selectedChoiceId = 0

有人能看出那段代码为什么会一直导致超时错误吗?

(注意:这段代码是一个运行多年的大型应用程序的一部分。所以我真的不认为这与连接字符串或类似的东西有任何关系。如果我进行上述更改,它就能稳定运行。)


它看起来更像是与SQL Server相关的问题,而不是EF问题。其他人建议更新统计信息http://serverfault.com/questions/419997/the-wait-operation-timed-out-when-running-sql-server-in-hyper-v。也可能是阻塞问题-您可以尝试读取未提交的数据https://dev59.com/-WAf5IYBdhLWcg3wZSBI#24699606。 - Vojtěch Dohnal
@VojtěchDohnal:我认为,最终错误发生在SQL Server中。问题实际上是关于这些行会导致SQL Server出现此问题的原因是什么。我非常怀疑这是一个阻塞问题。阻塞问题通常取决于其他代码,并且不像上面的代码那样一致。 - Jonathan Wood
初看,我怀疑 selectedChoiceId 的声明会导致 EF 向 SQL Server 发送一个更复杂的子查询,因此自然而然地删除它会执行得更快(因此没有超时)。我会附加分析器,捕获生成的 SQL,并调查其预估执行计划,以查看它命中了哪些索引以及如何命中。 - Nick Patsaris
尝试移动 var choice = (from c in li.OriginalPackageOption.CostingLineItems orderby (c.IsIncluded ?? false) ? -2 : (c.IsDefaultItem ?? false) ? -1 : c.Id select (int)c.OriginalPackageOptionChoiceId).FirstOrDefault() : 0 然后使用 let selectedChoiceId = choice,我认为子查询可能出现问题。 - Nilesh
@Nilesh:移动到哪里,这有什么区别和意义吗? - Jonathan Wood
2个回答

0

我认为这会给你更好的性能,但不确定它是否会解决问题:

let selectedChoiceId = li.OriginalPackageOptionId.HasValue
    ? (from c in li.OriginalPackageOption.CostingLineItems
        let cOrder = (c.IsIncluded ?? false) ? -2 : (c.IsDefaultItem ?? false) ? -1 : c.Id
        orderby cOrder
        select (int) c.OriginalPackageOptionChoiceId).FirstOrDefault()
    : 0

0

查询可以通过多种方式简化,这样数据库引擎就更容易进行优化。

首先,您可以删除一些空值检查(HasValue),因为它们在SQL中不相关,但会使生成的SQL膨胀。

其次,我认为涉及selectedChoiceId的此检查可以大大简化。这是我认为该语句可能如下所示:

from li in Repository.Query<CostingLineItem>()

let cid = (li.ParentCostingPackage != null) ?
    li.ParentCostingPackage.ParentCostingEvent.ProposalSection.Proposal.Costing.Id :
    li.ParentCostingEvent.ProposalSection.Proposal.Costing.Id

where cid == costingId &&
    li.OriginalProductId.HasValue &&
    li.Quantity > 0 &&  // no null check
    Classifications.Contains(li.OriginalProduct.ClassificationEnumIndex)

let selectedChoiceId = (from c in li.OriginalPackageOption.CostingLineItems
        orderby c.IsIncluded ? -2 : c.IsDefaultItem ? -1 : c.Id // no null checks
        select (int)c.OriginalPackageOptionChoiceId).FirstOrDefault()

where !li.OriginalPackageOptionId.HasValue || li.OriginalPackageOptionId == selectedChoiceId

let hasProviderAvailable = li.OriginalProductItem.ProductItemVendors.Any(
    piv => piv.ProductPricings.Any(pp => pp.ProductItemVendor.CompanyId != null || pp.ProductItemVendor.HotelId != null))

select new
{
    LineItem = li,
    ProductItem = li.OriginalProductItem,
    Product = li.OriginalProduct,
    Vendors = li.CostingLineItemVendors,
    HasProviderAvailable = hasProviderAvailable
}

对于其余部分,当然有一些常见的问题需要解决。随着数据库容量的增加,更好的索引可能变得更加重要。检查(并修复)数据库碎片也可能会产生显著影响。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接