我的任务是优化一些方法,同时查看代码,我发现了这个瓶颈(如果格式化似乎关闭,我道歉):
// ...
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
签名中),这产生了巨大的差异,但现在结果也不同了!
我知道性能改进是由于IEnumerable
和IQueryable
实现差异以及从数据库中提取不同的数据,但为什么最终结果不同?
我怀疑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
答案 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);