我是不是误解了 LINQ to SQL .AsEnumerable()?
Posted
技术标签:
【中文标题】我是不是误解了 LINQ to SQL .AsEnumerable()?【英文标题】:Am I misunderstanding LINQ to SQL .AsEnumerable()?我是否误解了 LINQ to SQL .AsEnumerable()? 【发布时间】:2010-08-02 16:43:53 【问题描述】:考虑这段代码:
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-to-Objects。相反,当我在 LINQPad 中执行此操作时,我看到相同的查询运行了 3 次。如果我用.ToList()
替换.AsEnumerable()
,它只会被查询一次。
我是否遗漏了 AsEnumerable
是什么/做什么?
【问题讨论】:
了解AsEnumerable()
行为的一个非常有用的问题
【参考方案1】:
调用AsEnumerable(
) 不会执行查询,而枚举它会执行。
IQueryable
是允许LINQ to SQL
发挥其魔力的接口。 IQueryable
实现了IEnumerable
,所以当您调用AsEnumerable()
时,您正在更改从那里调用的扩展方法,即从IQueryable
方法到IEnumerable
方法(即从LINQ to SQL
更改在这种特殊情况下,LINQ to Objects
)。但是您没有执行实际查询,只是更改了整个执行的方式。
要强制执行查询,您必须调用ToList()
。
【讨论】:
认为“什么都没有发生”当然是不正确的。虽然AsEnumerable
在调用它时不会评估查询,但它肯定会产生影响。对查询进一步调用的任何内容都将使用 LINQ to 对象进行评估,因此您无法将其他元素组合到将成为 SQL 语句一部分的查询(另一个 Where
或 OrderBy
或任何类似性质的元素)。
一个很好的例子来说明它的作用和你为什么要使用它:假设你正在构建一个查询并且第一个块以OrderBy(...)
结尾,现在类型是IOrderedEnumerable
所以以后你可以继续追加ThenBy(...)
,甚至稍后你可以说return originalQuery.AsEnumerable()
将它转换回普通的IEnumerable
我更喜欢做同样事情的 ToArray,除非你特别需要 ListToList()
更快,因此除非您的对象存在很长时间,否则请使用ToList
而不是ToArray
,请参阅***.com/a/16323412/691294
@Backwards_Dave 好吧,是的,我不同意这一点。似乎我驳斥的陈述现在已被删除,但一般情况是分配比需要更多的内存(即ToList()
)比确保适量的内存(即ToArray()
)更快。我的论点是,如果您不知道使用哪一个,请使用ToList()
,因为它的抽象性更高,并且不会降低性能。【参考方案2】:
是的。 AsEnumerable
所做的只是使Count
、Sum
和Average
函数在客户端执行(换句话说,它将把整个结果集返回给客户端,然后客户端将执行这些聚合,而不是在 SQL 中创建 COUNT()
SUM()
和 AVG()
语句)。
【讨论】:
但是OP的意思是他假设你说的是真的,但经验测试表明它不是。 -1 这根本不是真的。 IQueryable 实现了 IEnumerable,因此对 AsEnumerable 的调用是无操作的,不会强制执行查询。 @James,贾斯汀:你误会了。我从来没有说过AsEnumerable()
会导致查询评估,我说添加它唯一会做的是当聚合被评估时,它们将在客户端完成(整个结果集将在在客户端进行枚举,并计算聚合)而不是被翻译成SQL语句。
请注意,当您有 IOrderedEnumerable
时,您可能希望使用 AsEnumerable()
,因为您以某种类型的 OrderBy 结束了查询的第一部分。
@JustinNiessner 该评论显然是事实错误,静态类型转换 EVER 是如何NOP
的?它改变了整个执行方案......这很重要,因为 LINQ 是作为扩展方法(即静态类型)而不是继承(动态/运行时类型)构建的。【参考方案3】:
Justin Niessner's answer 完美。
我只想在这里引用一个 MSDN 的解释:.NET Language-Integrated Query for Relational Data
与 ToList() 和 ToArray() 不同,AsEnumerable() 运算符不会导致执行查询。它仍然被推迟。 AsEnumerable() 运算符仅更改查询的静态类型,将 IQueryable 转换为 IEnumerable,诱使编译器将查询的其余部分视为本地执行。
我希望这就是它的意思:
IQueryable-methods 到 IEnumerable-methods(即从 LINQ 到 SQL 到 LINQ 到 Objects 的变化
一旦它是 LINQ to Objects,我们就可以应用对象的方法(例如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】:嗯,你在正确的轨道上。问题是IQueryable
(AsEnumerable
调用之前的语句)也是IEnumerable
,因此该调用实际上是一个 nop。它需要强制它使用特定的内存数据结构(例如,ToList()
)来强制查询。
【讨论】:
【参考方案5】:我认为 ToList 会强制 Linq 从数据库中获取记录。然后,当您执行后续计算时,它们是针对内存中的对象完成的,而不是涉及数据库。
将返回类型保留为 Enumerable 意味着在执行计算的代码调用它之前不会获取数据。我猜这个敲击是数据库被击中了 3 次 - 每次计算一次,并且数据没有持久化到内存中。
【讨论】:
以上是关于我是不是误解了 LINQ to SQL .AsEnumerable()?的主要内容,如果未能解决你的问题,请参考以下文章
Linq To Sql Contains 参数数量最大不能超过 2100 怎么解决?