52

给定以下 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 子句。

这是否意味着整个“导入”表被缓存到内存中?如果表包含大量记录,这是否会降低性能?

帮助我了解这里幕后实际发生的事情。

4

4 回答 4

41

AsEnumerable的原因是

AsEnumerable(TSource)(IEnumerable(TSource)) 可用于在序列实现 IEnumerable(T) 但也有一组不同的可用公共查询方法时在查询实现之间进行选择

因此,当您Where之前调用该方法时,您调用的是WhereIEnumerable.Where. 该Where语句用于将 LINQ 转换为 SQL,新语句是Where采用IEnumerableIEnumerable枚举它并产生匹配项的语句。这就解释了为什么您会看到正在生成的不同 SQL。Where在将扩展应用到您的第二个代码版本之前,该表将从数据库中完整获取。这可能会造成严重的瓶颈,因为整个表必须在内存中,或者更糟的是,整个表必须在服务器之间传输。允许 SQL Server 执行Where并做它最擅长的事情。

于 2010-07-22T16:57:45.197 回答
8

在枚举被枚举通过的点,然后将查询数据库,并检索整个结果集。

部分解决方案可能是一种方式。考虑

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 被枚举、查询或以其他方式使用时,尽可能多的工作将传递给数据库,数据库将返回足够的数据以继续工作。假设确实不可能重写以便所有工作都可以发送到数据库,这可能是一个合适的折衷方案。

于 2010-07-23T18:15:47.883 回答
6

有三种实现方式AsEnumerable

DataTableExtensions.AsEnumerable

扩展 aDataTable给它一个IEnumerable接口,这样你就可以对DataTable.

Enumerable.AsEnumerable<TSource>ParallelEnumerable.AsEnumerable<TSource>

AsEnumerable<TSource>(IEnumerable<TSource>)除了将源的编译时类型从实现的类型更改IEnumerable<T>为自身之外,该方法没有任何作用IEnumerable<T>

AsEnumerable<TSource>(IEnumerable<TSource>)IEnumerable<T>当序列实现但也有一组不同的公共查询方法可用时,可用于在查询实现之间进行选择 。例如,给定一个Table实现 IEnumerable<T>并拥有自己的方法(如WhereSelect和 )的泛型类,SelectMany调用Where将调用 的公共Where方法 TableTable表示数据库表 的类型可以具有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));

现在服务器将返回所有数据,并且将Enumerable.Where使用从 Linq 到对象的方法,而不是使用查询提供程序的实现。

Entity Framework不知道怎么解释没关系SomeUnusualPredicate,我的函数会直接使用。(但是,这可能是一种低效的方法,因为所有行都将从服务器返回。)

于 2014-05-19T09:23:01.693 回答
2

我相信 AsEnumerable 只是告诉编译器要使用哪些扩展方法(在这种情况下是为 IEnumerable 而不是为 IQueryable 定义的扩展方法)。查询的执行仍会延迟,直到您调用 ToArray 或对其进行枚举。

于 2010-07-22T16:59:16.093 回答