了解 LINQ to SQL 中的 .AsEnumerable()
Posted
技术标签:
【中文标题】了解 LINQ to SQL 中的 .AsEnumerable()【英文标题】:Understanding .AsEnumerable() in LINQ to SQL 【发布时间】:2011-03-19 16:22:32 【问题描述】:给定以下 LINQ to SQL 查询:
var test = from i in Imports
where i.IsActive
select i;
解释的 SQL 语句是:
SELECT [t0].[id] AS [Id] .... FROM [Imports] AS [t0] WHERE [t0].[isActive] = 1
假设我想在 select 中执行一些无法转换为 SQL 的操作。我的理解是,实现此目的的常规方法是执行 AsEnumerable()
从而将其转换为可行的对象。
鉴于此更新的代码:
var test = from i in Imports.AsEnumerable()
where i.IsActive
select new
// Make some method call
;
并更新了 SQL:
SELECT [t0].[id] AS [Id] ... FROM [Imports] AS [t0]
请注意在执行的 SQL 语句中缺少 where 子句。
这是否意味着整个“Imports”表被缓存到内存中? 如果表包含大量记录,这是否会降低性能?
帮助我了解幕后实际发生的事情。
【问题讨论】:
看看另一个问题的例子:[在此处输入链接描述][1] [1]:***.com/a/14129116/1792434 【参考方案1】:AsEnumerable的原因是为了
AsEnumerable(TSource)(IEnumerable(TSource)) 可用于在查询之间进行选择 序列时的实现 实现 IEnumerable(T) 但也有 一组不同的公共查询 可用的方法
因此,当您之前调用Where
方法时,您调用的是与IEnumerable.Where
不同的Where
方法。 Where
语句用于 LINQ 转换为 SQL,新的 Where
是采用 IEnumerable
的 IEnumerable
语句,枚举它并产生匹配项。这就解释了为什么您会看到正在生成的不同 SQL。在将 Where
扩展名应用到您的代码的第二个版本之前,该表将从数据库中完整获取。这可能会造成严重的瓶颈,因为整个表必须在内存中,或者更糟的是,整个表必须在服务器之间传输。允许 SQL 服务器执行 Where
并做它最擅长的事情。
【讨论】:
所以它仍然有延迟执行的权利吗?以上面的查询为例,当我执行“test.Dump()”之类的操作时,整个“Imports”表不会只存储在内存中。对吗? @Mike Fielden 整个表将通过内存,因为 Where 正在枚举它。我不确定是否所有行都会同时加载到内存中。 @YuriyFaktorovich:那么它为什么存在呢?请告诉使用 AsEnumerable 的专业人士。谢谢 @Unbreakable 例如,当您想要应用查询实现不支持的过滤器(例如一些复杂的 lambda 表达式)时。您可以首先应用带有一些初始过滤的Where
方法,然后调用AsEnumerable
,然后调用另一个Where
来细化过滤条件(使用查询不支持的lambda)。通过这种方式,您可以克服查询实现的限制,同时仍然能够预先过滤查询,这样您就不会获取所有数据。
我刚刚意识到这基本上就是 Jon Hanna 在他的回答中所描述的。【参考方案2】:
我相信 AsEnumerable 只是告诉编译器使用哪些扩展方法(在这种情况下是为 IEnumerable 而不是为 IQueryable 定义的扩展方法)。 查询的执行仍会延迟,直到您调用 ToArray 或对其进行枚举。
【讨论】:
这没有任何意义。 AsEnumerable 是一种方法。它并没有特别“告诉编译器”任何事情。 是的,它是一个方法,但它的返回类型是 Enumerable,因此编译器在编译 LINQ 查询(Enumerable 查询)时将使用不同的方法。从文档中: AsEnumerable在枚举通过的点,然后将查询数据库,并检索整个结果集。
一个部分和部分的解决方案可能是一种方式。考虑
var res = (
from result in SomeSource
where DatabaseConvertableCriterion(result)
&& NonDatabaseConvertableCriterion(result)
select new result.A, result.B
);
假设 NonDatabaseConvertableCriterion 需要来自结果的字段 C。因为 NonDatabaseConvertableCriterion 就像它的名字所暗示的那样,所以这必须作为枚举来执行。但是,请考虑:
var partWay =
(
from result in SomeSource
where DatabaseConvertableCriterion(result)
select new result.A, result.B, result.C
);
var res =
(
from result in partWay.AsEnumerable()
where NonDatabaseConvertableCriterion select new result.A, result.B
);
在这种情况下,当枚举、查询或以其他方式使用 res 时,将尽可能多的工作传递给数据库,数据库将返回足够的数据以继续工作。假设确实不可能重写,让所有的工作都可以发送到数据库,这可能是一个合适的折衷方案。
【讨论】:
【参考方案4】:AsEnumerable
的三种实现方式。
DataTableExtensions.AsEnumerable
扩展 DataTable
以为其提供 IEnumerable
接口,以便您可以针对 DataTable
使用 Linq。
Enumerable.AsEnumerable<TSource>
和 ParallelEnumerable.AsEnumerable<TSource>
AsEnumerable<TSource>(IEnumerable<TSource>)
方法无效 除了将源的编译时类型从一个类型更改为 实现IEnumerable<T>
到IEnumerable<T>
本身。
AsEnumerable<TSource>(IEnumerable<TSource>)
可以用来选择 序列实现时的查询实现之间IEnumerable<T>
但也有一组不同的公共查询方法 可用的。例如,给定一个泛型类Table
,它实现IEnumerable<T>
并有自己的方法如Where
、Select
和SelectMany
,对Where
的调用将调用公共的Where
方法Table
。表示数据库表的Table
类型可以有Where
将谓词参数作为表达式树的方法 并将树转换为 SQL 以进行远程执行。如果远程执行 不需要,例如因为谓词调用本地 方法,AsEnumerable<TSource>
方法可以用来隐藏 自定义方法,而不是使用标准查询运算符 可用。
换句话说。
如果我有一个
IQueryable<X> sequence = ...;
来自 LinqProvider,例如 Entity Framework,我也这样做,
sequence.Where(x => SomeUnusualPredicate(x));
该查询将在服务器上组合和运行。这将在运行时失败,因为 EntityFramework 不知道如何将 SomeUnusualPredicate
转换为 SQL。
如果我希望它改为使用 Linq to Objects 运行语句,我会这样做,
sequence.AsEnumerable().Where(x => SomeUnusualPredicate(x));
现在服务器将返回所有数据,并且将使用来自 Linq to Objects 的Enumerable.Where
,而不是查询提供程序的实现。
Entity Framework 不知道如何解释SomeUnusualPredicate
没关系,我的函数将被直接使用。 (但是,这可能是一种低效的方法,因为所有行都将从服务器返回。)
【讨论】:
以上是关于了解 LINQ to SQL 中的 .AsEnumerable()的主要内容,如果未能解决你的问题,请参考以下文章
理解LINQ to SQL中的.AsEnumerable()