.ToList()、.AsEnumerable()、AsQueryable()之间有什么区别?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (14)

我知道LINQ到实体和LINQto对象的一些不同之处,这是第一个实现的。IQueryable和第二个工具IEnumerable我的问题范围在EF 5之内。

我的问题是,这三种方法的技术区别是什么?如.ToList().AsQueryable()

  1. 这些方法到底意味着什么?
  2. 是否有任何性能问题或某些东西会导致使用一个而另一个?
  3. 为什么一个人会使用,例如,.ToList().AsQueryable()而不是.AsQueryable()?
提问于
用户回答回答于

集中讨论AsEnumerableAsQueryable提到ToList():

这些方法是做什么的?

AsEnumerableAsQueryable铸造或转换为IEnumerableIQueryable分别。

  • 当源对象已经实现目标接口时,源对象本身将被返回,但是铸造到目标界面。换句话说:类型没有改变,但是编译时类型是。
  • 当源对象未实现目标接口时,源对象为转换转换为实现目标接口的对象。因此,类型和编译时类型都会更改。

让我举几个例子来说明这一点。

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

让我们尝试一个任意的Linq到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经常用于从任何IQueryable实现LINQtoObjects(L2O),主要是因为前者不支持L2O所具有的函数。

例如,在实体框架查询中,我们只能使用有限数量的方法。因此,例如,如果我们需要在查询中使用自己的方法之一,我们通常会编写如下

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

ToList-转换IEnumerable<T>转到List<T>-也经常用于这一目的。使用优势AsEnumerablev.V.ToList那是AsEnumerable不执行查询。AsEnumerable保留延迟执行,并且不构建通常无用的中间列表。

另一方面,当需要强制执行LINQ查询时,ToList也能做到这一点。

AsQueryable可用于使LINQ语句中的可枚举集合接受表达式。

注意事项!

AsEnumerable就像毒品一样。这是一个快速的解决方法,但需要付出代价,而且不能解决根本的问题。

在许多堆栈溢出的答案中,我看到人们正在申请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结果集的长度和宽度都会减少。

真正的解决办法是:

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

这些方法没有做什么?

现在有个重要的警告。当你做的时候

context.Observations.AsEnumerable()
                    .AsQueryable()

您将最终得到表示为IQueryable...。(因为这两种方法只转换而不转换)。

但当你这么做的时候

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

结果如何?

Select产生一个WhereSelectEnumerableIterator...。这是一个内部.net类,它实现IEnumerableIQueryable...。因此,转换到另一种类型已经发生,并且随后的AsQueryable再也不能返回原始源了。

这意味着使用AsQueryable无法神奇地注入查询提供程序将它的特定特性变成可枚举的。假设你知道

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

WHERE条件将永远不会转换为SQL。AsEnumerable()然后是LINQ语句,明确地切断了与实体框架查询提供程序的连接。

我故意展示这个例子,因为我在这里看到了一些问题,比如人们试图“注入”Include通过调用AsQueryable。它编译和运行,但是它什么也不做,因为基础对象没有Include实现不再。

用户回答回答于

ToList()

  • 立即执行查询

AsEnDigable()

  • 延迟(稍后执行查询)
  • 参数:Func<TSource, bool>
  • 负载每一个记录到应用程序内存,然后处理/过滤它们。*从表1到内存,然后选择第一个X元素(在本例中,它所做的:Linqto-SQL+Linqto-Object)

AsQueryable()

  • 延迟(稍后执行查询)
  • 参数:Expression<Func<TSource, bool>>
  • 将表达式转换为T-SQL(与特定提供程序一起),远程查询并将结果加载到应用程序内存中。
  • 这就是为什么DbSet(在实体框架中)也继承了IQueryable来获得高效的查询。
  • 不要加载每条记录,例如,如果采取(5),它将生成选择前5*后台的SQL。这意味着这种类型对SQL数据库更友好,这就是为什么这种类型通常具有更高的性能,并且在处理数据库时推荐使用。
  • 所以AsQueryable()通常比AsEnumerable()因为它首先生成T-SQL,其中包括Linq中的所有WHERE条件。

扫码关注云+社区