我知道第一个实现的 LINQ to Entities 和 LINQ to Objects 的一些区别IQueryable第二个实现IEnumerable我的问题范围在EF 5以内。

我的问题是这 3 种方法的技术差异是什么?.ToList().AsQueryable()

  1. 这些方法到底意味着什么?

  2. Is there any performance issue or something that would lead to the use of one over the other?

  3. 例如,为什么要使用.ToList().AsQueryable()代替.AsQueryable()

答案

关于这一点有很多话要说。AsEnumerableAsQueryable并提及ToList()一路上。

这些方法有什么作用?

AsEnumerableAsQueryable投射或转换为IEnumerable或者IQueryable, 分别。投射或转换有一个原因:

  • 当源对象已经实现了目标接口时,返回源对象本身,但是投掷到目标界面。

  • 当源对象没有实现目标接口时,源对象是转换的到实现目标接口的对象中。

让我用一些例子来展示这一点。乔恩·斯基特提供):

void ReportTypeProperties<T>(T obj)
{
    Console.WriteLine("Compile-time type: {0}", typeof(T).Name);
    Console.WriteLine("Actual type: {0}", obj.GetType().Name);
}

让我们尝试任意 linq-to-sqlTable<T>,它实现了IQueryable:

ReportTypeProperties(context.Observations);
ReportTypeProperties(context.Observations.AsEnumerable());
ReportTypeProperties(context.Observations.AsQueryable());

结果:

Compile-time type: Table`1
Actual type: Table`1

Compile-time type: IEnumerable`1
Actual type: Table`1

Compile-time type: IQueryable`1
Actual type: Table`1

您会看到表类本身总是返回,但其表示形式发生了变化。

现在是一个实现的对象IEnumerable, 不是IQueryable:

var ints = new[] { 1, 2 };
ReportTypeProperties(ints);
ReportTypeProperties(ints.AsEnumerable());
ReportTypeProperties(ints.AsQueryable());

结果:

Compile-time type: Int32[]
Actual type: Int32[]

Compile-time type: IEnumerable`1
Actual type: Int32[]

Compile-time type: IQueryable`1
Actual type: EnumerableQuery`1

就在那里。AsQueryable()已将数组转换为EnumerableQuery,其中"代表IEnumerable<T>集合作为IQueryable<T>数据源。"(MSDN)。

什么用途?

AsEnumerable 经常用于从任何IQueryableLINQ to object (L2O) 的实现,主要是因为前者不支持 L2O 具有的功能。AsEnumerable() 对 LINQ 实体有什么影响?

例如,在实体框架查询中,我们只能使用有限数量的方法。

var query = context.Observations.Select(o => o.Id)
                   .AsEnumerable().Select(x => MySuperSmartMethod(x))

ToList – 将一个IEnumerable<T>到一个List<T>– 也经常用于此目的。AsEnumerableToList就是它AsEnumerable不执行查询。AsEnumerable保留延迟执行并且不会构建通常无用的中间列表。

另一方面,当需要强制执行 LINQ 查询时,ToList可以是做到这一点的一种方法。

AsQueryable 可用于使可枚举集合接受 LINQ 语句中的表达式。我真的需要在集合上使用 AsQueryable() 吗?

注意药物滥用!

AsEnumerable就像药物一样起作用。

在许多 Stack Overflow 答案中,我看到人们在申请AsEnumerable修复 LINQ 表达式中不受支持的方法的几乎所有问题。

context.MyLongWideTable // A table with many records and columns
       .Where(x => x.Type == "type")
       .Select(x => new { x.Name, x.CreateDate })

…一切都被整齐地翻译成 SQL 语句过滤器Where) 和项目 ((Select)。也就是说,SQL结果集的长度和宽度分别都会降低。

现在假设用户只想查看日期部分CreateDate。在实体框架中,您会很快发现…

.Select(x => new { x.Name, x.CreateDate.Date })

…不支持(在写作时)。啊,幸运的是AsEnumerable使固定:

context.MyLongWideTable.AsEnumerable()
       .Where(x => x.Type == "type")
       .Select(x => new { x.Name, x.CreateDate.Date })

当然,它可能会运行。Where第一的:

context.MyLongWideTable
       .Where(x => x.Type == "type").AsEnumerable()
       .Select(x => new { x.Name, x.CreateDate.Date })

但仍然首先获取所有列,并在内存中完成投影。

真正的解决方法是:

context.MyLongWideTable
       .Where(x => x.Type == "type")
       .Select(x => new { x.Name, DbFunctions.TruncateTime(x.CreateDate) })

(但这需要更多的知识……)

这些方法不做什么?

恢复 IQueryable 功能

现在有一个重要的警告。

context.Observations.AsEnumerable()
                    .AsQueryable()

您最终将得到表示为的源对象IQueryable

但当你这样做时

context.Observations.AsEnumerable().Select(x => x)
                    .AsQueryable()

结果会怎样?

Select产生一个WhereSelectEnumerableIteratorIEnumerable,不是IQueryableAsQueryable永远无法再返回原始来源。

这意味着使用AsQueryablenot a way to magically inject a query provider及其具体特征变成可枚举。

var query = context.Observations.Select(o => o.Id)
                   .AsEnumerable().Select(x => x.ToString())
                   .AsQueryable()
                   .Where(...)

where 条件永远不会被翻译成 SQL。AsEnumerable()随后的 LINQ 语句明确切断与实体框架查询提供程序的连接。

我故意展示这个例子是因为我在这里看到过一些问题,例如人们试图"注入"Include通过调用将功能放入集合中AsQueryableInclude不再执行。

执行

两个都AsQueryableAsEnumerable不执行(或枚举 ) 源对象。IQueryableIEnumerable,只不过是"等待发生的枚举"。ToList()

这意味着执行IEnumerable通过调用获得AsEnumerable在一个IQueryable对象,将执行底层IQueryableIEnumerable将再次执行IQueryable

具体实施

到目前为止,这仅涉及Queryable.AsQueryableEnumerable.AsEnumerable扩展方法。

事实上,一个特定的常见例子AsEnumerable扩展方法是DataTableExtensions.AsEnumerableDataTable不实施IQueryable或者IEnumerable,因此常规扩展方法不适用。

来自: stackoverflow.com