了解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 ,并且拥有自己的方法(如WhereSelectSelectMany ,则调用Where将调用Table的Public Where方法。 表示数据库表的Tabletypes可以有一个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或枚举它。

Interesting Posts