Entity framework 什么';.ToList()、.AsEnumerable()和AsQueryable()之间的区别是什么?

Entity framework 什么';.ToList()、.AsEnumerable()和AsQueryable()之间的区别是什么?,entity-framework,entity-framework-4,linq-to-entities,entity-framework-5,Entity Framework,Entity Framework 4,Linq To Entities,Entity Framework 5,我知道LINQ对实体和LINQ对对象的一些区别,第一个实现了IQueryable,第二个实现了IEnumerable,我的问题范围在EF 5之内 我的问题是这三种方法的技术区别是什么?我看到在很多情况下,它们都能工作。我还看到使用它们的组合,如.ToList().AsQueryable() 这些方法到底意味着什么 是否存在任何性能问题或会导致使用其中一个而不是另一个的问题 例如,为什么要使用.ToList().AsQueryable()而不是.AsQueryable() ToList()将成为内

我知道LINQ对实体和LINQ对对象的一些区别,第一个实现了
IQueryable
,第二个实现了
IEnumerable
,我的问题范围在EF 5之内

我的问题是这三种方法的技术区别是什么?我看到在很多情况下,它们都能工作。我还看到使用它们的组合,如
.ToList().AsQueryable()

  • 这些方法到底意味着什么

  • 是否存在任何性能问题或会导致使用其中一个而不是另一个的问题

  • 例如,为什么要使用
    .ToList().AsQueryable()
    而不是
    .AsQueryable()

  • ToList()将成为内存中的所有内容,然后您将处理它。 因此,ToList()。其中(应用某些筛选器)在本地执行。 AsQueryable()将远程执行所有操作,即将其上的筛选器发送到数据库进行应用。 Queryable在执行之前不会做任何事情。然而,托利斯特立即执行死刑

    还有,看看这个答案

    编辑: 此外,在您的情况下,一旦执行了ToList(),则后续的每个操作都是本地操作,包括AsQueryable()。一旦开始在本地执行,就无法切换到远程。
    希望这能让它更清楚一点。

    关于这一点,有很多话要说。让我重点关注
    AsEnumerable
    AsQueryable
    ,并在此过程中提到
    ToList()

    这些方法的作用是什么?
    AsEnumerable
    AsQueryable
    分别转换为
    IEnumerable
    IQueryable
    。我说“铸造”或“转化”是有原因的:

    • 当源对象已经实现了目标接口时,源对象本身将返回,但会强制转换到目标接口。换句话说:类型没有改变,但编译时类型是

    • 当源对象未实现目标接口时,源对象将转换为实现目标接口的对象。因此,类型和编译时类型都发生了更改

    让我用一些例子来说明这一点。我有一个报告编译时类型和对象的实际类型()的小方法:

    void ReportTypeProperties(T obj)
    {
    WriteLine(“编译时类型:{0}”,typeof(T).Name);
    WriteLine(“实际类型:{0}”,obj.GetType().Name);
    }
    
    让我们尝试一个任意的linq to sql
    ,它实现了
    IQueryable

    ReportTypeProperties(context.Observations);
    ReportTypeProperties(context.Observations.AsEnumerable());
    ReportTypeProperties(context.Observations.AsQueryable());
    
    结果是:

    编译时类型:表'1 实际类型:表` 1 编译时类型:IEnumerable`1 实际类型:表` 1 编译时类型:IQueryable`1 实际类型:表` 1 您可以看到,表类本身总是返回的,但其表示形式会发生变化

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

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

    编译时类型:Int32[] 实际类型:Int32[] 编译时类型:IEnumerable`1 实际类型:Int32[] 编译时类型:IQueryable`1 实际类型:EnumerableQuery`1 就在那里
    AsQueryable()
    已将数组转换为,它“将
    IEnumerable
    集合表示为
    IQueryable
    数据源”(MSDN)

    有什么用?
    AsEnumerable
    经常用于从任何
    IQueryable
    实现切换到LINQ to objects(L2O),这主要是因为前者不支持L2O具有的功能。有关更多详细信息,请参阅

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

    var query = context.Observations.Select(o => o.Id)
                       .AsEnumerable().Select(x => MySuperSmartMethod(x))
    
    ToList
    ——它将
    IEnumerable
    转换为
    列表
    ——也经常用于此目的。使用
    AsEnumerable
    ToList
    相比的优点是
    AsEnumerable
    不执行查询
    AsEnumerable
    保留延迟执行,不构建通常无用的中间列表

    另一方面,当需要强制执行LINQ查询时,
    ToList
    可以作为一种方法

    AsQueryable
    可用于使可枚举集合接受LINQ语句中的表达式。有关更多详细信息,请参见此处:

    关于药物滥用的说明!
    AsEnumerable
    就像药物一样有效。这是一个快速解决方案,但要付出代价,而且不能解决根本问题

    在许多堆栈溢出的答案中,我看到人们应用
    aseneumerable
    来解决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
    fix:

    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) })
    
    context.Observations.AsEnumerable()
                        .AsQueryable()
    
    context.Observations.AsEnumerable().Select(x => x)
                        .AsQueryable()
    
    var query = context.Observations.Select(o => o.Id)
                       .AsEnumerable().Select(x => x.ToString())
                       .AsQueryable()
                       .Where(...)
    
    void DoSomething<T>(IEnumerable<T> objects){
        var single = objects.First(); //load everything into memory before .First()
        ...
    }
    
    void DoSomething<T>(IEnumerable<T> objects){
        T single;
        if (objects is IQueryable<T>)
            single = objects.AsQueryable().First(); // SELECT TOP (1) ... is used
        else
            single = objects.First();
    
    }
    
    T single =  objects is IQueryable<T> q? 
                        q.First(): 
                        objects.First();