【问题标题】:Would using an extension method to query an IEnumerable be equivalent to using IQueryable?使用扩展方法查询 IEnumerable 是否等同于使用 IQueryable?
【发布时间】:2015-09-17 14:28:50
【问题描述】:

我了解 IQueryable 和 IEnumerable 之间的主要区别。 IQueryable 在数据库中执行查询并返回过滤后的结果,而 IEnumerable 将完整的结果集带入内存并在那里执行过滤查询。

我不认为使用扩展方法来查询变量的初始分配会导致它像 IQueryable 一样在数据库中执行,但我只是想确定一下。

这段代码会导致从数据库中返回People的完整结果集,然后在内存中进行过滤:

(上下文中的 People 属性是 DbSet 类型)

IEnumerable<Person> people = context.People;
Person person = people.Where(x => x.FirstName == "John");

即使我在将项目分配给我的变量之前添加下面的过滤作为扩展方法,我假设这段代码应该与上面的代码一样工作,并在过滤之前将完整的结果集带回内存对吧?

Person person = context.People.Where(x => x.FirstName == "John");

编辑:

感谢大家的回复。我修改了代码示例以显示我的意思(删除了第二段代码中的 IEnumerable)。

另外,澄清一下,context.People 是 DbSet 类型,它实现了 IQueryable 和 IEnumerable。所以我实际上不确定调用了哪个 .Where 方法。 IntelliSense 告诉我它是 IQueryable 版本,但这可以信任吗?直接使用上下文的 DbSet 时总是这样吗?

【问题讨论】:

  • 那么context.People 的类型是什么?我怀疑它是IQueryable&lt;Person&gt;,在这种情况下不,它们不一样——你的第一个代码将调用Enumerable.Where,你的第二个代码将调用Queryable.Where
  • 假设context.PeopleIQueryable 而不是IEnumerable,那么第二个选项会更快,因为您的数据库将返回更少的数据。将 Where 应用于可查询时,您仍然有可查询
  • 你的理解有误。是 IQueryable 的实现以及使用它的代码决定了它的行为。该接口仅定义了支持针对不同提供程序的类似 linq 操作的类型的形状。
  • 差异的原因是您将people 分配给IEnumerable&lt;Person&gt;。尝试使用varIQueryable&lt;Person&gt;,结果会有所不同。

标签: c# linq ienumerable iqueryable


【解决方案1】:
IEnumerable<Person> people = context.People;
Person person = people.Where(x => x.FirstName == "John");

... 将执行IEnumerable<T>.Where 方法扩展,它接受Func&lt;TSource, bool&gt; predicate 参数,强制在内存中进行过滤。

相比之下……

IEnumerable<Person> people = context.People.Where(x => x.FirstName == "John");

...将执行IQueryable<T>.Where 方法扩展,它接受Expression&lt;Func&lt;TSource, bool&gt;&gt; predicate 参数。请注意,这是一个表达式,而不是委托,它允许它将where 条件转换为数据库条件。

因此,调用哪种扩展方法确实会有所不同。

【讨论】:

    【解决方案2】:

    IQueryable 适用于表达式。它实际上是一个查询构建器,无需执行任何操作即可累积有关查询的信息……直到您需要从中获取值的那一刻,当:

    • 使用目标语言(例如 SQL)从累积的表达式生成查询
    • 查询被执行,通常在数据库上,
    • 结果被转换回 C# 对象。

    IEnumerable 扩展适用于预编译的函数。当你需要它的价值时:

    • 执行这些函数中的 C# 代码。

    两者很容易混淆,因为:

    • 两者都有名称相似的扩展函数,
    • 表达式和函数的 lambda 语法是相同的 - 因此您无法区分它们,
    • 使用“var”声明变量会删除数据类型(通常是使用哪个接口的唯一线索)。

      IQueryable<int> a;
      IEnumerable<int> b;
      
      int x1 = a.FirstOrDefault(i => i > 10);    // Expression passed in
      int x2 = b.FirstOrDefault(i => i > 10);    // Function passed in
      

    具有相同名称的扩展方法通常做同样的事情(因为它们是这样写的)但有时它们不会。

    所以答案是:不,它们不等价。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-12
      • 1970-01-01
      • 2012-01-16
      相关资源
      最近更新 更多