gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 04:17:52 29 4
gpt4 key购买 nike

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

我的问题是这 3 种方法的技术差异是什么?我看到在许多情况下它们都有效。我也看到使用它们的组合,如 .ToList().AsQueryable() .

  • 这些方法究竟是什么意思?
  • 是否有任何性能问题或会导致使用一个而不是另一个的问题?
  • 为什么要使用,例如,.ToList().AsQueryable()而不是 .AsQueryable() ?
  • 最佳答案

    关于这一点,有很多话要说。让我专注于AsEnumerableAsQueryable并提及 ToList()一路上。
    这些方法有什么作用?AsEnumerableAsQueryable强制转换或转换为 IEnumerableIQueryable , 分别。我说强制转换或转换是有原因的:

  • 当源对象已经实现了目标接口(interface)时,源对象本身被返回但转换到目标接口(interface)。换句话说:类型没有改变,但编译时类型改变了。
  • 当源对象没有实现目标接口(interface)时,将源对象转换为实现目标接口(interface)的对象。所以类型和编译时类型都改变了。

  • 让我用一些例子来说明这一点。我有这个小方法报告编译时类型和对象的实际类型( courtesy Jon Skeet ):
    void ReportTypeProperties<T>(T obj)
    {
    Console.WriteLine("Compile-time type: {0}", typeof(T).Name);
    Console.WriteLine("Actual type: {0}", obj.GetType().Name);
    }
    让我们尝试一个任意的 linq-to-sql Table<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 切换LINQ to objects (L2O) 的实现,主要是因为前者不支持 L2O 具有的功能。更多详情请见 What is the effect of AsEnumerable() on a LINQ Entity? .
    例如,在 Entity Framework 查询中,我们只能使用有限数量的方法。因此,例如,如果我们需要在查询中使用我们自己的方法之一,我们通常会编写类似
    var query = context.Observations.Select(o => o.Id)
    .AsEnumerable().Select(x => MySuperSmartMethod(x))
    ToList – 转换 IEnumerable<T>List<T> – 也经常用于此目的。使用优势 AsEnumerable对比 ToListAsEnumerable不执行查询。 AsEnumerable保留延迟执行并且不会构建通常无用的中间列表。
    另一方面,当需要强制执行 LINQ 查询时, ToList可以是一种方式来做到这一点。
    AsQueryable 可用于使可枚举集合接受 LINQ 语句中的表达式。详情请看这里: Do i really need use AsQueryable() on collection? .
    注意药物滥用! 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 })
    ...所有内容都被巧妙地转换为过滤 ( Where ) 和项目 ( Select ) 的 SQL 语句。也就是说,SQL 结果集的长度和宽度都分别减少了。
    现在假设用户只想看到 CreateDate 的日期部分.在 Entity Framework 中,您会很快发现...
    .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产生 WhereSelectEnumerableIterator .这是一个内部 .Net 类,它实现了 IEnumerable ,不是 IQueryable .因此发生了向另一种类型的转换以及随后的 AsQueryable再也无法返回原始来源。
    这意味着使用 AsQueryable不是一种神奇地注入(inject)查询提供程序的方法 以其特有的功能成为可枚举的。假设你这样做
    var query = context.Observations.Select(o => o.Id)
    .AsEnumerable().Select(x => x.ToString())
    .AsQueryable()
    .Where(...)
    where 条件永远不会被翻译成 SQL。 AsEnumerable()紧随其后的 LINQ 语句明确切断了与 Entity Framework 查询提供程序的连接。
    我特意展示了这个例子,因为我在这里看到了一些问题,例如人们试图“注入(inject)” Include通过调用 AsQueryable 将功能转换为集合.它编译并运行,但它什么也不做,因为底层对象没有 Include实现了。
    执行
    两者 AsQueryableAsEnumerable不要执行(或枚举)源对象。他们只改变他们的类型或表示。都涉及接口(interface), IQueryableIEnumerable , 只不过是“等待发生的枚举”。它们在被迫执行之前不会被执行,例如,如上所述,通过调用 ToList() .
    这意味着执行 IEnumerable通过拨打 AsEnumerable 获得在 IQueryable 上对象,将执行底层 IQueryable . IEnumerable 的后续执行将再次执行 IQueryable .这可能非常昂贵。
    具体实现
    到目前为止,这只是关于 Queryable.AsQueryable Enumerable.AsEnumerable 扩展方法。但是当然任何人都可以编写具有相同名称(和函数)的实例方法或扩展方法。
    其实就是一个具体的常见例子 AsEnumerable扩展方法是 DataTableExtensions.AsEnumerable . DataTable不实现 IQueryableIEnumerable ,因此常规扩展方法不适用。

    关于entity-framework - .ToList()、.AsEnumerable()、AsQueryable() 之间有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17968469/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com