【问题标题】:Returning IEnumerable when the object is an IQueryable当对象是 IQueryable 时返回 IEnumerable
【发布时间】:2013-03-22 00:49:12
【问题描述】:

我对 IEnumerable 和 IQueryable 以及与 Entity Framework 的用法交换有点困惑。我已经尝试尽可能多地在这个主题上进行谷歌搜索,但它似乎没有帮助。这是我的情况。

我有一个函数(MyClass 是一个EntityObject):

IEnumerable<MyClass> GetMyClasses(int id) {
    return GetQuery().Where(p => p.Id==id);
}

IQueryable<MyClass> GetQuery() {
    // returns an IQueryable list here
}

现在我的问题是。鉴于我在这里返回一个 IEnumerable 而不是 IQueryable,当使用 ToList() 执行语句时,生成的底层 SQL 是否会考虑 Id == id 语句,因此仅返回过滤器items 还是会将数据库的所有对象带入内存并在该列表上执行 Where 子句?

有任何帮助,甚至是指向描述此内容的资源的指针吗?

【问题讨论】:

  • 这取决于 EF 返回的 IQueryable 的实现,因此正确答案将特定于 EF 实现。我将Where 应用于查询,但我不确定。

标签: c# entity-framework ienumerable iqueryable


【解决方案1】:

当使用 ToList() 执行语句时,生成的底层 SQL 是否会考虑 Id == id 语句,因此仅返回过滤器项

简短的回答是“是”。

只有Enumerable.ToList&lt;T&gt;(this IEnumumerable&lt;T&gt; e),没有IQueryable&lt;T&gt;的对应项

GetMyClasses 方法中的所有内容都将作为IQueryable&lt;T&gt; 操作,但之后添加的限制将在内存中。

IEnumerable<MyClass> GetMyClasses(int id) {
    return GetQuery().Where(p => p.Id==id); // this always will work as IQueryable and handled by provider (in case of EF - the DB query will be performed)
}

IEnumerable<MyClass> MoreRestrictions(int id) {
     return GetMyClasses(id)
         .ToList(); // result list will be filtered by Id
}

IEnumerable<MyClass> MoreRestrictions(int id) {
     return GetMyClasses(id)
         .Where(x=>x.IsActive) // this where will be performed in memory on results filtered by Id.
         .ToList(); 
}

方法ToList 和Where 的工作方式如下

//There is only one implementation of ToList()
public List<T> ToList<T>(this IEnumerable<T> e) 
{
    var list = new List<T>();
    var enumerator = e.GetEnumerator();
    while (enumerator.MoveNext())
    {
        var item = e.Current;
        list.Add(item);
    }
    return list;
}

//Implementation for IEnumerable
public IEnumerable<T> Where<T>(this IEnumerable<T> e, Predicate predicate) 
{
    var enumerator = e.GetEnumerator();
    while (enumerator.MoveNext())
    {
        var item = e.Current;
        if (predicate(item))
            yield return item;
    }
}

//Implementation for IQueryable
public IQueryable<T> Where<T>(this IQueryable<T> e, Expression<Predicate> predicate) 
{
    MethodBase method = ...; // create generic method of Queryable.Where()
    return e.Provider
        .CreateQuery<T>(Expression.Call(null, method, e.Expression, Expression.Quote(predicate)));
}

IQueryable 的GetEnumerator 方法进行查询实现。

【讨论】:

  • 太棒了,这就是我所希望的。为了澄清起见,您能否解释一下为什么您的第二个 MoreRestrictions 示例将一些查询作为 SQL 语句执行,而另一些则作为内存执行?
  • @dreza 有两个原因:它将受到预编译谓词的限制,而不是基于表达式。接受 IEnumerable 的 Where 的实现不关心 IQueryable 并在内存中进行过滤。
  • 我认为这是有道理的。似乎与下面约翰斯的回答相矛盾,但不是 100%??。将进一步研究您上面提到的 Where 子句。干杯
【解决方案2】:

你把这弄得太复杂了。考虑以下几点:

public class Base { }

public class Derived : Base { }

public Base Method() {
    return GetOtherMethod();
}

public Derived GetOtherMethod()
{
    return new Derived ();
}

您想知道Method 的返回值的特征。它将是Derived 的一个实例,但您只能将其用作Base,除非您将其转换为Derived。

因此,您的GetMyClasses 将返回一个IQueryable&lt;MyClass&gt;,该IQueryable&lt;MyClass&gt; 只能用作IEnumerable&lt;MyClass&gt;。

【讨论】:

  • 那么当 ToList() 出现在 IEnumerable 上时,生成的 SQL 会包含 Where id == 5 还是会出现在内存中?
  • 它不是 IEnumerable。这是一个只有 IEnumerable 成员可用的 IQueryable。它的行为类似于 IQueryable,因为它就是这样。
  • 好的,谢谢。基于此,在他的第二个 MoreRestrictions() 示例中的 hazziks 回答的情况下,我会认为返回的结果将全部转换为 SQL,因为我的类型实际上是一个 Queryable 对象??
  • 这里没有魔法。我的示例 Base 和 Derived 类会发生这种转换吗?不,那么它就不会在这里发生。这是一个简单的多态性案例。返回的IQueryable 的GetEnumerator() 方法将实现标准的IQueryable 语义,即使GetEnumerator() 方法被引用为IEnumerable&lt;T&gt;.GetEnumerator()。
  • 好吧,在我的小世界里,你的回答似乎与 hazziks 相矛盾。但也许他们都在说同样的话,而我就是不明白。感谢您帮助澄清。
【解决方案3】:

这篇文章很有帮助: IQueryable vs IEnumerable in LINQ-to-SQL.

引用那篇文章,“根据 MSDN 文档,对 IQueryable 进行的调用是通过构建内部表达式树来操作的。 “这些扩展 IQueryable(Of T) 的方法不直接执行任何查询。相反,它们的功能是构建一个 Expression 对象,这是一个表示累积查询的表达式树。”'

表达式树在 C# 和 .NET 平台上是一个非常重要的结构。 (它们通常很重要,但 C# 使它们非常有用。)为了更好地理解差异,我建议阅读 expressions 和 statements 之间的差异in the official C# 5.0 specification here. For Advanced分支到 lambda 演算的理论概念,表达式支持将方法作为一等对象。 IQueryable 和 IEnumerable 之间的区别集中在这一点上。 IQueryable 构建表达式树,而 IEnumerable 不构建表达式树,至少对于我们这些不在微软秘密实验室工作的人来说不是一般意义上的。

这是另一篇非常有用的文章,详细介绍了推式与拉式的区别。 (“推”与“拉”是指数据流的方向。Reactive Programming Techniques for .NET and C#

这是一篇非常好的文章,详细介绍了语句 lambda 和表达式 lambda 之间的区别,并更深入地讨论了表达式树的概念:Revisiting C# delegates, expression trees, and lambda statements vs. lambda expressions.。

【讨论】:

  • 感谢您的链接和信息
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-26
相关资源
最近更新 更多