我误解了LINQ to SQL .AsEnumerable()?

时间:2010-08-02 16:43:53

标签: c# .net linq linq-to-sql

考虑以下代码:

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执行查询,然后使用CountSumAverage的LINQ到对象。相反,当我在LINQPad中执行此操作时,我看到相同的查询运行三次。如果我将.AsEnumerable()替换为.ToList(),则只会查询一次。

我是否遗漏了AsEnumerable /是什么?

5 个答案:

答案 0 :(得分:56)

调用AsEnumerable()不执行查询,枚举它。

IQueryable是允许LINQ to SQL执行其魔法的界面。 IQueryable实现了IEnumerable,因此当您调用AsEnumerable()时,您正在更改从那里调用的扩展方法,即从IQueryable - 方法更改为IEnumerable } -methods(即在这种特殊情况下从LINQ to SQL更改为LINQ to Objects)。但是你执行实际查询,只是改变 它将被完整地执行。

要强制执行查询,您必须调用ToList()

答案 1 :(得分:13)

是。 AsEnumerable所做的只是CountSumAverage函数在客户端执行(换句话说,它会带回整个结果集)对于客户端,客户端将执行这些聚合,而不是在SQL中创建COUNT() SUM()AVG()语句。

答案 2 :(得分:4)

嗯,你走在正确的轨道上。问题是IQueryable(在AsEnumerable调用之前的语句是什么)也是IEnumerable,因此该调用实际上是一个nop。它需要强制它到特定的内存数据结构(例如,ToList())来强制查询。

答案 3 :(得分:3)

Justin Niessner's answer是完美的。

我只想在这里引用一个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.jonskeetAsEnumerable在以下情况下非常方便:

  

数据库中查询的某些方面,然后在.NET中进行一些操作 - 特别是如果有些方面你基本上无法在LINQ to SQL(或者你正在使用的任何提供程序)中实现。

它还说:

  

我们所做的只是改变通过我们的查询从IQueryable传播到IEnumerable的序列的编译时类型 - 但这意味着编译器将使用Enumerable中的方法(获取委托,并在LINQ中执行对象)而不是Queryable中的对象(使用表达式树,通常执行进程外)。

最后,另请参阅此相关问题:Returning IEnumerable vs. IQueryable

答案 4 :(得分:1)

我认为ToList强制Linq从数据库中获取记录。然后,当您执行正在进行的计算时,它们将针对内存对象而不是涉及数据库。

将返回类型保留为Enumerable意味着在执行计算的代码调用数据之前不会获取数据。我想这就是数据库被击中三次 - 每次计算一次,数据不会持久存储到内存中。