返回IEnumerable <T> 与IQueryable相比 <T> [关闭]

蓝咒 提交于 2019-12-13 11:22:18

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

返回IQueryable<T>IEnumerable<T>之间有什么区别?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

两者都会延迟执行吗?什么时候应该优先于另一个?


#1楼

一般来说,我会建议如下:

  • 返回IQueryable<T>如果要使用您的方法启用开发人员来优化您在执行之前返回的查询。

  • 如果要传输一组对象以进行枚举,则返回IEnumerable

想象一下IQueryable就是它的IQueryable - 数据的“查询”(如果你愿意,可以改进)。 IEnumerable是一组可以枚举的对象(已经接收或创建过)。


#2楼

通常,您希望保留查询的原始静态类型,直到重要为止。

因此,您可以将变量定义为'var'而不是IQueryable<>IEnumerable<>并且您将知道您没有更改类型。

如果你从IQueryable<> ,你通常希望将它保持为IQueryable<>直到有一些令人信服的理由来改变它。 这样做的原因是您希望为查询处理器提供尽可能多的信息。 例如,如果您只使用10个结果(您已调用Take(10) ),那么您希望SQL Server知道这一点,以便它可以优化其查询计划并仅向您发送您将使用的数据。

将类型从IQueryable<>更改为IEnumerable<>一个令人信服的理由可能是您正在调用某个扩展函数,即您的特定对象中的IQueryable<>的实现无法处理或处理效率低下。 在这种情况下,您可能希望将类型转换为IEnumerable<> (通过分配给IEnumerable<>类型的变量或通过使用AsEnumerable扩展方法),以便您调用的扩展函数最终成为Enumerable类而不是Queryable类。


#3楼

我最近遇到了IEnumerable IQueryable 。 首先使用的算法执行IQueryable查询以获得一组结果。 然后将这些项传递给foreach循环,将项实例化为实体框架(EF)类。 然后在Linq to Entity查询的from子句中使用此EF类,导致结果为IEnumerable

我是EF和Linq for Entities的新手,所以需要一段时间来弄清楚瓶颈是什么。 使用MiniProfiling,我找到了查询,然后将所有单个操作转换为单个IQueryable Linq for Entities查询。 IEnumerable耗时15秒, IQueryable需要0.5秒才能执行。 涉及到三个表,在阅读之后,我认为IEnumerable查询实际上形成了一个三表交叉产品并过滤结果。

尝试使用IQueryables作为经验法则,并对您的工作进行分析,以使您的更改可衡量。


#4楼

有一篇博客文章,内容简要介绍了IEnumerable<T>滥用如何极大地影响LINQ查询性能: 实体框架:IQueryable与IEnumerable

如果我们深入挖掘并查看源代码,我们可以看到IEnumerable<T>显然有不同的扩展方法:

// Type: System.Linq.Enumerable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Enumerable
{
    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> predicate)
    {
        return (IEnumerable<TSource>) 
            new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
    }
}

IQueryable<T>

// Type: System.Linq.Queryable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Queryable
{
    public static IQueryable<TSource> Where<TSource>(
        this IQueryable<TSource> source, 
        Expression<Func<TSource, bool>> predicate)
    {
        return source.Provider.CreateQuery<TSource>(
            Expression.Call(
                null, 
                ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(
                    new Type[] { typeof(TSource) }), 
                    new Expression[] 
                        { source.Expression, Expression.Quote(predicate) }));
    }
}

第一个返回可枚举迭代器,第二个通过IQueryable源中指定的查询提供程序创建查询。


#5楼

之前已经说过很多,但是以更技术性的方式回到根源:

  1. IEnumerable 是内存中可以枚举的对象集合 - 一个内存序列,可以迭代(在foreach循环中使其变得容易,尽管你只能使用IEnumerator )。 它们按原样驻留在内存中。
  2. IQueryable 是一个表达式树 ,它可以在某个时刻被翻译成其他东西, 并能够枚举最终结果 。 我想这就是让大多数人感到困惑的原因。

他们显然有不同的内涵。

IQueryable表示一个表达式树(简单地称为查询),一旦调用发布API,它将被底层查询提供者转换为其他内容树,如LINQ聚合函数(Sum,Count等)或ToList [Array,Dictionary, ...]。 IQueryable对象还实现IEnumerableIEnumerable<T>这样如果它们表示查询,则可以迭代该查询的结果。 这意味着IQueryable不必仅是查询。 正确的术语是表达树

现在,如何执行这些表达式以及它们转向什么都取决于所谓的查询提供程序(表达式执行程序,我们可以认为它们)。

Entity Framework世界(这是神秘的底层数据源提供程序或查询提供程序)中, IQueryable表达式被转换为本机T-SQL查询。 Nhibernate与他们做类似的事情。 您可以按照LINQ:构建IQueryable Provider链接中描述的概念编写自己的概念,并且您可能希望为产品存储提供程序服务提供自定义查询API。

所以基本上, IQueryable对象一直在构建,直到我们显式释放它们并告诉系统将它们重写为SQL或其他任何东西并向下发送执行链以进行后续处理。

好像要延迟执行它是一个LINQ功能,用于在内存中保存表达式树方案,并且只有在需要对序列调用某些API(相同的Count,ToList等)时才将其发送到执行中。

两者的正确使用在很大程度上取决于您针对特定案例所面临的任务。 对于众所周知的存储库模式,我个人选择返回IList ,即IEnumerable over Lists(索引器等)。 因此,我的建议是仅在存储库中使用IQueryable ,并在代码中的任何其他地方使用IEnumerable。 没有说可IQueryable性问题, IQueryable崩溃和破坏关注分离原则。 如果从存储库中返回表达式,则消费者可以按照自己的意愿使用持久层。

混乱的一点点:)(来自评论中的讨论))它们都不是内存中的对象,因为它们本身并不是真正的类型,它们是一种类型的标记 - 如果你想深入了解它。 但是,将MSnumerables视为内存中的集合而将IQueryables视为表达式树,这是有意义的(这就是为什么即使是MSDN也这样说)。 关键是IQueryable接口继承IEnumerable接口,因此如果它表示查询,则可以枚举该查询的结果。 枚举导致与IQueryable对象关联的表达式树被执行。 所以,事实上,如果没有内存中的对象,你就无法真正调用任何IEnumerable成员。 如果你这样做,它会进入那里,无论如何,如果它不是空的。 IQueryables只是查询,而不是数据。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!