与IEnumerable

时间:2017-07-19 05:23:12

标签: c# linq linq-to-sql linq-to-entities

我的任务是优化一些方法,同时查看代码,我发现了这个瓶颈(如果格式化似乎关闭,我道歉):

// ...
    IEnumerable<Card> storageCards = 
        Db.StorageCards
          .Where(x => x.Active && x.DocumentType == (int)DocumentType.Import);

    // excludeLastDate is bool
    storageCards = storageCards.Where(x => (excludeLastDate && x.Date < toD) 
                                        || (!excludeLastDate && x.Date <= toD));

    return LocationPriceData(storageCards, locationId.Value);
}

private Dictionary<int, decimal> LocationPriceData(IEnumerable<Card> storageCards
                                                   , int locationId)
{
    // sc.LocationId, sc.ProductId are nullable int
    // sc.Price is nullable decimal
    // sc.Date is datetime, not null
    var result = from sc in storageCards
                    where sc.LocationId == locationId
                    group sc by sc.ProductId
                        into g
                        let price = g.OrderByDescending(t => t.Date)
                                     .ThenByDescending(t => t.Id)
                                     .FirstOrDefault(t => t.Price.HasValue)
                        where price != null
                        select new
                        {
                            ProductId = g.Key.Value,
                            Price = price.LevelInputPrice.Value
                        };

    return result.ToDictionary(x => x.ProductId, x => x.Price);
}

为了改进它,我将storageCard类型格式IEnumerable<Card>更改为IQueryable<Card>(也在LocationPriceData签名中),这产生了巨大的差异,但现在结果也不同了!

我知道性能改进是由于IEnumerableIQueryable实现差异以及从数据库中提取不同的数据,但为什么最终结果不同?

我怀疑group部分,但由于它正在比较一个可空的int,我无法看到不同结果的原因? Db是MS SQL服务器。

哦,在这两种情况下,实现都发生在return result.ToDictionary(x => x.ProductId, x => x.Price);

修改

当更改为IQueryable时,生成的查询与我的预期不同。 Cross Apply没有任何订购!

以下代码背后的逻辑永远不会在DB上执行:

                    let price = g.OrderByDescending(t => t.Date)
                                 .ThenByDescending(t => t.Id)
                                 .FirstOrDefault(t => t.Price.HasValue)

这是生成的sql:

exec sp_executesql N'SELECT 
[Element1].[Id] AS [Id], 
[Project2].[ProductId] AS [ProductId], 
[Element1].[LevelInputPrice] AS [LevelInputPrice]
FROM   (SELECT 
    [Distinct1].[ProductId] AS [ProductId]
    FROM ( SELECT DISTINCT 
        [Extent1].[ProductId] AS [ProductId]
        FROM [dbo].[StorageCard] AS [Extent1]
        WHERE ((([Extent1].[Active] = 1) AND (10 = [Extent1].[DocumentType])) OR (40 = [Extent1].[DocumentType]) OR ((70 = [Extent1].[DocumentType]) AND ([Extent1].[InputQuantity] > cast(0 as decimal(18))))) AND (((@p__linq__0 = 1) AND ([Extent1].[Date] < @p__linq__1)) OR ((@p__linq__2 <> cast(1 as bit)) AND ([Extent1].[Date] <= @p__linq__3))) AND ([Extent1].[LocationId] = @p__linq__4)
    )  AS [Distinct1] ) AS [Project2]
CROSS APPLY  (SELECT TOP (1) 
    [Extent2].[Id] AS [Id], 
    [Extent2].[Date] AS [Date], 
    [Extent2].[DocumentType] AS [DocumentType], 
    [Extent2].[InputQuantity] AS [InputQuantity], 
    [Extent2].[LevelInputPrice] AS [LevelInputPrice], 
    [Extent2].[Active] AS [Active], 
    [Extent2].[LocationId] AS [LocationId], 
    [Extent2].[ProductId] AS [ProductId]
    FROM [dbo].[StorageCard] AS [Extent2]
    WHERE ((([Extent2].[Active] = 1) AND (10 = [Extent2].[DocumentType])) OR (40 = [Extent2].[DocumentType]) OR ((70 = [Extent2].[DocumentType]) AND ([Extent2].[InputQuantity] > cast(0 as decimal(18))))) AND (((@p__linq__0 = 1) AND ([Extent2].[Date] < @p__linq__1)) OR ((@p__linq__2 <> cast(1 as bit)) AND ([Extent2].[Date] <= @p__linq__3))) AND ([Extent2].[LocationId] = @p__linq__4) AND (([Project2].[ProductId] = [Extent2].[ProductId]) OR (([Project2].[ProductId] IS NULL) AND ([Extent2].[ProductId] IS NULL))) AND ([Extent2].[LevelInputPrice] IS NOT NULL) ) AS [Element1]
WHERE [Element1].[Id] IS NOT NULL',N'@p__linq__0 bit,@p__linq__1 datetime2(7),@p__linq__2 bit,@p__linq__3 datetime2(7),@p__linq__4 int',@p__linq__0=0,@p__linq__1='2017-07-19 08:43:52.6901840',@p__linq__2=0,@p__linq__3='2017-07-19 08:43:52.6901840',@p__linq__4=11

1 个答案:

答案 0 :(得分:1)

我完全不熟悉LINQ中的查询语法。我感觉ORM可能不支持let语句,因为它的复杂性。但奇怪的是,它完全从SQL中省略,而不是生成错误。

尝试给出一个镜头,而不是:

var result = storageCards
    .Where(sc => sc.LocationId == locationId)
    .GroupBy(sc => sc.ProductId)
    .Select(g => new {
        ProductId = g.Key.Value,
        Price = g.OrderByDescending(t => t.Date)
                 .ThenByDescending(t => t.Id)
                 .Where(t => t.Price.HasValue)
                 .Select(t => t.LevelInputPrice.Value)
                 .FirstOrDefault()
    })
    .Where(a => a.Price != null);