2017-07-19 32 views
0

我的任務不同IQueryable的結果是優化的一些方法,並一邊看着代碼,我發現這個瓶頸(我很抱歉,如果格式化似乎關閉):自IEnumerable

// ... 
    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沒有任何訂購!

背後下面的代碼的邏輯從來沒有在數據庫中執行:

    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 
+0

把你的問題https://codereview.stackexchange.com/ –

+1

縱觀探查,查詢在'ToDictionary'線接收? –

+0

@TadijaBagarić..無論如何,這似乎就是你所說的;所以無視我的意見:) – Rob

回答

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); 
+1

我試過類似的let語句,而Linq2Sql很好。 – sgmoore

+0

Tnx,而不是我需要的答案,但它讓我知道答案。 –