考虑以下代码:
var query = db.Table
.Where(t => SomeCondition(t))
.AsEnumerable();
int recordCount = query.Count();
int totalSomeNumber = query.Sum();
decimal average = query.Average();
假设query
需要很长时间才能运行。我需要获取记录计数,返回总SomeNumber
,并在结束时取平均值。根据我的阅读,我认为.AsEnumerable()
将使用LINQ-to-SQL执行查询,然后使用Count
,Sum
和Average
的LINQ到对象。相反,当我在LINQPad中执行此操作时,我看到相同的查询运行三次。如果我将.AsEnumerable()
替换为.ToList()
,则只会查询一次。
我是否遗漏了AsEnumerable
/是什么?
答案 0 :(得分:56)
调用AsEnumerable(
)不执行查询,枚举它。
IQueryable
是允许LINQ to SQL
执行其魔法的界面。 IQueryable
实现了IEnumerable
,因此当您调用AsEnumerable()
时,您正在更改从那里调用的扩展方法,即从IQueryable
- 方法更改为IEnumerable
} -methods(即在这种特殊情况下从LINQ to SQL
更改为LINQ to Objects
)。但是你不执行实际查询,只是改变 它将被完整地执行。
要强制执行查询,您必须调用ToList()
。
答案 1 :(得分:13)
是。 AsEnumerable
所做的只是Count
,Sum
和Average
函数在客户端执行(换句话说,它会带回整个结果集)对于客户端,客户端将执行这些聚合,而不是在SQL中创建COUNT()
SUM()
和AVG()
语句。
答案 2 :(得分:4)
嗯,你走在正确的轨道上。问题是IQueryable
(在AsEnumerable
调用之前的语句是什么)也是IEnumerable
,因此该调用实际上是一个nop。它需要强制它到特定的内存数据结构(例如,ToList()
)来强制查询。
答案 3 :(得分:3)
我只想在这里引用一个MSDN解释:.NET Language-Integrated Query for Relational Data
与ToList()和ToArray()不同,AsEnumerable()运算符不会导致执行查询。它仍然是推迟的。 AsEnumerable()运算符只是更改查询的静态类型,将IQueryable转换为IEnumerable,欺骗编译器将查询的其余部分视为本地执行。
我希望这就是:
IEnumerable方法的IQueryable方法(即从LINQ更改为SQL到LINQ到对象
一旦它是对象的LINQ,我们就可以应用对象的方法(例如ToString())。这是关于LINQ的一个常见问题的解释 - Why LINQ to Entities does not recognize the method 'System.String ToString()?
根据ASENUMERABLE - codeblog.jonskeet,AsEnumerable
在以下情况下非常方便:
数据库中查询的某些方面,然后在.NET中进行一些操作 - 特别是如果有些方面你基本上无法在LINQ to SQL(或者你正在使用的任何提供程序)中实现。
它还说:
我们所做的只是改变通过我们的查询从IQueryable传播到IEnumerable的序列的编译时类型 - 但这意味着编译器将使用Enumerable中的方法(获取委托,并在LINQ中执行对象)而不是Queryable中的对象(使用表达式树,通常执行进程外)。
最后,另请参阅此相关问题:Returning IEnumerable vs. IQueryable
答案 4 :(得分:1)
我认为ToList强制Linq从数据库中获取记录。然后,当您执行正在进行的计算时,它们将针对内存对象而不是涉及数据库。
将返回类型保留为Enumerable意味着在执行计算的代码调用数据之前不会获取数据。我想这就是数据库被击中三次 - 每次计算一次,数据不会持久存储到内存中。