【问题标题】:LINQ query performing very slow on even small collectionsLINQ 查询在即使是小集合上也执行得非常慢
【发布时间】:2017-09-21 04:55:51
【问题描述】:

我有一个非常奇怪的问题,我的 LINQ 查询执行速度如此之慢。代码如下:

var user = ctx.Users.FirstOrDefault(x => x.Username== username);

ViewBag.Items = user.Items.GroupBy(x => x.ItemId).Select(pr => new SalesViewModel
{
 ImageURL = pr.Select(x=>x.ImageURL).FirstOrDefault(),
 Title= pr.Select(x=>x.Title).FirstOrDefault(),
 Sales = pr.Select(x=>x.Transactions.Sum(y=>y.QuantitySold)).FirstOrDefault()
 })
 .OrderByDescending(x=>x.Sales)
 .ToList();

因此,用户对象包含一个 ICollection 集合 Items,其中包含 1300 个项目。这些项目中的每一个都包含另一个称为“事务”集合的 ICollection...

我注意到每个都最多有 20-25 个事务,所以项目内的集合并没有那么大......

我在这里做错了什么,为什么 LINQ 需要 30-40 秒来处理此代码?

有什么办法可以改善吗?

【问题讨论】:

  • 如果ItemId是Items表的PK,则不需要分组
  • @octavioccl 不幸的是 :( ...这是一个单独的属性
  • 我从ctx 猜测这是实体的 linq?如果是这样,请使用分析器查看正在生成的 SQL 语句。
  • @stuartd 是的,是的,好的,我会看看那个 thx :)
  • 听起来像是懒加载导致的N+1查询问题。

标签: c# asp.net asp.net-mvc linq c#-4.0


【解决方案1】:

请将您的 linq 查询更改为:

var user = ctx.Users.FirstOrDefault(x => x.Username== username);

ViewBag.Items = user.Items.GroupBy(x => x.ItemId).Select(pr => new SalesViewModel
{
    ImageURL = pr.FirstOrDefault(x=>x.ImageURL),
    Title= pr.FirstOrDefault(x=>x.Title),
    Sales = pr.FirstOrDefault(x=>x.Transactions.Sum(y=>y.QuantitySold))
})
.OrderByDescending(x=>x.Sales)
.ToList();

【讨论】:

  • Muhammad,当您按 ItemID 分组时,您如何实际访问 ImageURL/TItle/Transactions 属性?
  • pr 是一个组,而不是Item
  • 如果您只需要IEnumerable,则可以通过删除.ToList() 使其更快
  • @MuhammadQasim 说它无法将 lambda 表达式转换为预期的委托
  • 您的答案使用的代码较少,但语义相同,因此无法解决原始代码的任何问题。
【解决方案2】:

试试这个方法:

var ids = user.Items
    .GroupBy(x => x.ItemId)
    .Where(x=> x.Any())
    .Select(pr => pr.First().Id); /* No ToList yet */

user
    .Items
    .Where(x=> ids.Contains(x.Id)) // <--
    .Select(pr => new SalesViewModel
    {
        ImageURL = x.ImageURL,
        Title= x.Title,
        Sales = x.Transactions.Sum(y=>y.QuantitySold)
    })
    .ToList();

方法是,从昂贵的GroupBy 中读取主键并将其用作主查询的子查询。在 LINQPad 或 SSMS 中试试这个,看看这是否会有所不同。

【讨论】:

  • 它说 .select 在当前上下文中不存在?
  • @User987 对不起,我更新了答案,第一个查询现在有一个额外的Where 并更新了Select
  • 由于某种原因,它仍然说 select 在当前上下文中不存在:D
  • @User987 太棒了!告诉我进展如何。
【解决方案3】:

正如 Ivan Stoev 在 cmets 中所写,它应该是由延迟加载引起的 N+1 查询问题。这里的答案是在对交易求和之前使用 include 语句并加载子实体。

var user = ctx.Users.FirstOrDefault(x => x.Username== username).Include("Items").Include("Transactions");

【讨论】:

    猜你喜欢
    • 2014-01-21
    • 2021-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-16
    • 1970-01-01
    相关资源
    最近更新 更多