鉴于以下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()会立即执行查询。
答案 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,000,000个项目,并将其添加到列表中。然后,最后,它将从该大型列表中获取前10个项目。
第二个版本只需要迭代前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(),它将采用这十个元素和将它们作为具体列表返回。)