LINQ ToList()。Take(10)vs Take(10)。ToList()哪一个生成更高效的查询

时间:2010-12-08 16:36:57

标签: c# linq linq-to-sql

鉴于以下LINQ声明,哪个更有效?

ONE:

public List<Log> GetLatestLogEntries()
{
    var logEntries = from entry in db.Logs
                 select entry;
    return logEntries.ToList().Take(10);
}

二:

public List<Log> GetLatestLogEntries()
{
    var logEntries = from entry in db.Logs
                 select entry;
    return logEntries.Take(10).ToList();
}

我知道.ToList()会立即执行查询。

5 个答案:

答案 0 :(得分:20)

第一个版本甚至不会编译 - 因为Take的返回值是IEnumerable<T>,而不是List<T>。所以你需要它:

public List<Log> GetLatestLogEntries()
{
    var logEntries = from entry in db.Logs
                 select entry;
    return logEntries.ToList().Take(10).ToList();
}

这将获取数据库中的所有数据并将其转换为列表,然后获取前10个条目,然后再次将其转换为列表。

Take(10)在数据库中出现(即第二种形式)对我来说当然看起来便宜很多......

请注意,没有Queryable.ToList()方法 - 您最终会调用Enumerable.ToList()来获取所有条目。换句话说,对ToList 的调用不会参与SQL翻译,而Take会这样做。

另请注意,在此处使用查询表达式也没有多大意义。我把它写成:

public List<Log> GetLatestLogEntries()
{
    return db.Log.Take(10).ToList();
}

请注意,您可能需要OrderBy来电 - 否则它只需要找到它找到的前10个条目,这可能不是最新的......

答案 1 :(得分:2)

第二个版本将更高效(在时间和内存使用方面)。例如,假设您有一个包含1,000,000个项目的序列:

  1. 第一个版本遍历所有1,000,000个项目,并将其添加到列表中。然后,最后,它将从该大型列表中获取前10个项目。

  2. 第二个版本只需要迭代前10个项目,然后将它们添加到列表中。 (其余999,990项甚至不需要考虑。)

答案 2 :(得分:2)

您的第一个选项无效,因为.Take(10)会将其转换为IEnumerable<Log>。您的返回类型为List<Log>,因此您必须执行return logEntries.ToList().Take(10).ToList(),效率更低。

通过执行.ToList().Take(10),您强制.Take(10)成为对象的LINQ,而另一种方式可以将过滤器传递给数据库或其他基础数据源。换句话说,如果您首先执行.ToList(),则必须从数据库传输所有对象并将其分配到内存中。然后你过滤到前10个。如果你在谈论数百万个数据库行(和对象),你可以想象这是非常低效和不可扩展的。

第二个也将立即运行,因为你有.ToList(),所以没有区别。

答案 3 :(得分:1)

这个怎么样?

I have 5000 records in "items"

版本1:

  IQueryable<T> items = Items; // my items
  items = ApplyFilteringCriteria(items, filter); // my filter BL 
  items = ApplySortingCriteria(items, sortBy, sortDir); // my sorting BL
  items = items.Skip(0);
  items = items.Take(25);
  return items.ToList();

这需要:服务器上20秒

版本2:

  IQueryable<T> items = Items; // my items
  items = ApplyFilteringCriteria(items, filter); // my filter BL 
  items = ApplySortingCriteria(items, sortBy, sortDir); // my sorting BL
  List<T> x = items.ToList();
  items = x.Skip(0).ToList();
  items = x.Take(25).ToList();
  return x;

这需要:服务器上1秒

你觉得现在怎么样?知道为什么吗?

答案 4 :(得分:0)

第二种选择。

第一个将评估整个可枚举,将其扼杀到List();然后设置迭代器,它将迭代前十个对象,然后退出。

第二个首先设置Take()迭代器,所以无论后来发生什么,只会评估10个对象并将其发送到“下游”处理(在本例中为ToList(),它将采用这十个元素和将它们作为具体列表返回。)