关于c#:当对象是IQueryable时返回IEnumerable

Returning IEnumerable when the object is an IQueryable

我对IEnumerable和IQueryable以及实体框架中的用法的交替使用有点困惑。我试过尽可能多地在这个问题上谷歌搜索,但似乎没有帮助。这是我的情况。

我有一个函数(MyClassEntityObject):

1
2
3
4
5
6
7
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子句?

有什么帮助,甚至是指向描述这个的资源的指针吗?


这篇文章很有帮助:linq to sql中的iqueryable与ienumerable。

引用这篇文章,"根据msdn文档,通过构建内部表达式树来调用iQueryable。"这些扩展IQueryable(of T)的方法不直接执行任何查询。相反,它们的功能是构建表达式对象,该对象是表示累积查询的表达式树。""

表达式树是C和.NET平台上非常重要的结构。(一般来说,它们很重要,但C使它们非常有用。)为了更好地理解这一区别,我建议阅读官方C 5.0规范中表达式和语句之间的差异。对于分支到lambda微积分的高级理论概念,表达式支持将方法作为一级对象。iqueryable和IEnumerable之间的区别集中在这一点上。iqueryable构建表达式树,而IEnumerable不构建表达式树,至少对于我们这些不在微软秘密实验室工作的人来说不构建表达式树。

这是另一篇非常有用的文章,详细介绍了推和拉的区别。(通过"推"和"拉",我指的是数据流的方向。.NET和C的反应式编程技术#

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


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

1
2
3
4
5
6
7
8
9
10
11
12
public class Base { }

public class Derived : Base { }

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

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

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

因此,您的GetMyClasses将返回一个IQueryable,它只能用作IEnumerable


when the statement is executed with a ToList() will the underlying SQL generated take into account the Id == id statement and so only return filter items

简短的回答是"是"。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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的作用如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//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方法查询物化。