当对象是IQueryable时返回IEnumerable

本文关键字:返回 IEnumerable IQueryable 对象 | 更新日期: 2023-09-27 18:05:18

我对IEnumerable和IQueryable以及与实体框架的用法互换有点困惑。我试着在谷歌上尽可能多地搜索这个主题,但似乎没有帮助。这就是我的情况。

我有一个函数(MyClassEntityObject):

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语句,因此只返回过滤器项,或者它会将数据库的所有对象带入内存并在该列表上执行Where子句?

有任何帮助,甚至指向描述这一点的资源吗?

当对象是IQueryable时返回IEnumerable

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

简短的回答是"是"。

只有Enumerable.ToList<T>(this IEnumumerable<T> e),没有对应的IQueryable<T>

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

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(); 
}

方法ToListWhere的工作原理如下

//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方法实现查询物化

你把事情弄得太复杂了。考虑以下内容:

public class Base { }
public class Derived : Base { }
public Base Method() {
    return GetOtherMethod();
}
public Derived GetOtherMethod()
{
    return new Derived ();
}

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

因此,您的GetMyClasses将返回IQueryable<MyClass>,这将只能作为IEnumerable<MyClass>使用。

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

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

表达式树是c#和。net平台上非常重要的结构。(它们通常很重要,但c#使它们非常有用。)为了更好地理解它们之间的区别,我建议阅读官方c# 5.0规范中表达式语句之间的区别。对于扩展到lambda演算的高级理论概念,表达式支持将方法作为一级对象。IQueryable和IEnumerable的区别就在这一点上。IQueryable可以构建表达式树,而IEnumerable不能,至少对于我们这些不在微软秘密实验室工作的人来说是这样。

这是另一篇非常有用的文章,从推和拉的角度详细说明了差异。(通过"push"与"拉,"我指的是数据流的方向。.NET和c#响应式编程技术

这是一篇非常好的文章,详细介绍了语句lambda和表达式lambda之间的区别,并更深入地讨论了表达式压力的概念:重新审视c#委托、表达式树和lambda语句与lambda表达式…

相关文章: