了解LINQ to SQL中的.AsEnumerable()
鉴于以下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语句中缺lesswhere子句。
这是否意味着整个“导入”表被caching到内存? 如果表中包含大量的logging,这是否会降低性能?
帮助我了解幕后的实际情况。
AsEnumerable的原因是
当一个序列实现IEnumerable(T)时,AsEnumerable(TSource)(IEnumerable(TSource))可以用于在查询实现之间进行select,但是也有一组不同的公共查询方法
所以当你在调用Where方法之前,你正在从IEnumerable.Where调用一个不同的Where方法。 Where语句用于LINQ转换为SQL,新的Where是IEnumerable接受IEnumerable的枚举,枚举它并生成匹配的项目。 这就解释了为什么你看到不同的SQL正在生成。 在扩展名将被应用到你的第二个版本的代码之前,表将从数据库中完整地获取。 这可能造成严重的瓶颈,因为整个表必须在内存中,或者更糟的是整个表必须在服务器之间传送。 允许SQL服务器执行Where,并执行它最擅长的操作。
在枚举枚举的地方,数据库将被查询,并检索整个结果集。
一个零件的解决scheme可以是这样的。 考虑
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被枚举,查询或以其他方式使用时,尽可能多的工作将被传递给数据库,这将返回足够的时间来继续工作。 假设确实不可能重写,所有的工作都可以发送到数据库,这可能是一个合适的妥协。
有三个AsEnumerable
实现。
DataTableExtensions.AsEnumerable
扩展一个DataTable
给它一个IEnumerable
接口,所以你可以使用Linq对DataTable
。
Enumerable.AsEnumerable<TSource>
和ParallelEnumerable.AsEnumerable<TSource>
除了将源的编译时types从实现
IEnumerable<T>
的types更改为IEnumerable<T>
本身之外,AsEnumerable<TSource>(IEnumerable<TSource>)
方法无效。当一个序列实现
IEnumerable<T>
时,AsEnumerable<TSource>(IEnumerable<TSource>)
可以用来在查询实现之间进行select,但也可以使用不同的公共查询方法。 例如,如果给定一个实现了IEnumerable<T>
的generics类Table
,并且拥有自己的方法(如Where
,Select
和SelectMany
,则调用Where
将调用Table
的PublicWhere
方法。 表示数据库表的Table
types可以有一个Where
方法,它将谓词参数作为expression式树并将树转换为SQL以供远程执行。 如果不需要远程执行,例如因为谓词调用本地方法,则可以使用AsEnumerable<TSource>
方法来隐藏自定义方法,而是使标准查询运算符可用。
换一种说法。
如果我有一个
IQueryable<X> sequence = ...;
从一个LinqProvider,像entity framework,而我呢,
sequence.Where(x => SomeUnusualPredicate(x));
该查询将被组成并在服务器上运行。 这将在运行时失败,因为EntityFramework不知道如何将SomeUnusualPredicate
转换为SQL。
如果我想要用Linq来运行声明而不是对象,
sequence.AsEnumerable().Where(x => SomeUnusualPredicate(x));
现在服务器将返回所有数据,并且将使用从Linq到Objects的Enumerable.Where
而不是Query Provider的实现。
entity framework不知道如何解释SomeUnusualPredicate
并不SomeUnusualPredicate
,我的函数将直接使用。 (但是,这可能是一种效率低下的方法,因为所有的行将从服务器返回。)
我相信AsEnumerable只是告诉编译器使用哪些扩展方法(在这种情况下是为IEnumerable而不是IQueryable定义的)。 查询的执行仍然是延迟,直到您调用ToArray或枚举它。