【问题标题】:Differences when using IEnumerable and IQueryable as a type of ObjectSet使用 IEnumerable 和 IQueryable 作为 ObjectSet 类型时的差异
【发布时间】:2011-03-24 08:11:57
【问题描述】:

据我了解,当我在 IQueryable 上使用 LINQ 扩展方法(使用 lambda 表达式语法)时,实际上是 ObjectSet 实例,它们被转换为 LINQ to SQL 查询。我的意思是那个命令

IQueryable<User> users = db.UserSet;
var users32YearsOld = users.Where(user => user.Age == 32);

完全一样

IQueryable<User> users = db.UserSet;   
var users32YearsOld = from user in users where user.Age == 32 select user;

所以在他们users32YearsOld被枚举为循环或类似之前,他们都不会访问数据库。 (希望我理解正确)。

但是,如果我不将 ObjectSet 屏蔽为 IQueryable 而是屏蔽为 IEnumerable 会发生什么?那么如果它的类型是IEnumerable 呢?

IEnumerable<User> users = db.UserSet;
var users32YearsOld = users.Where(user => user.Age == 32);

它会立即访问数据库吗(如果是,那么什么时候?就在第一行或第二行)?或者它是否会像之前的命令一样在users32YearsOld 被枚举之前不会命中数据库?如果我改用following会有什么不同吗?

IEnumerable<User> users = db.UserSet;
var users32YearsOld = from user in users where user.Age == 32 select user;

谢谢

【问题讨论】:

    标签: c# .net entity-framework ienumerable iqueryable


    【解决方案1】:

    取消删除我的答案,因为我刚刚对其进行了测试,它的工作原理与我描述的完全一样:

    由于没有枚举,上述查询都不会命中数据库。 IQueryable 查询和IEnumerable 查询之间的区别在于,在IQueryable 的情况下,过滤将在数据库服务器上执行,而在IEnumerable 的情况下,所有对象都将从数据库加载到内存中,并且过滤将在 .NET 代码(linq-to-objects)中完成。可以想象,这通常是性能杀手。

    我在我的项目中写了简单的测试:

    [TestMethod]
    public void Test()
    {
        // ObjectQuery<Department> converted ot IEnumerable<Department>
        IEnumerable<Department> departmetns = CreateUnitOfWork().GetRepository<Department>().GetQuery();
        // No query execution here - Enumerable has also deffered exection
        var query = departmetns.Where(d => d.Id == 1); 
        // Queries ALL DEPARTMENTS here and executes First on the retrieved result set
        var result = departmetns.First(); 
    }
    

    【讨论】:

    • “没有提到的查询会命中数据库,因为没有枚举”与前 2 个答案相矛盾。到目前为止,我从你那里得到了非常好的答案,所以我倾向于更认真地对待你的答案。还是真的“没有枚举”? IEnumerable 在调用 Where(u =&gt; u.Age == 32) 时没有枚举?那是怎么回事(我可以想象它的工作原理类似于IQueryable,但知道比猜测更好)?
    • 我对其进行了测试并取消了我的答案,因为在 SQL Profiler 中检查后我发现我的答案是正确的。
    • 正如我所说,我第一次认真对待它......所以当我在一些 IEnumerable 上调用 Where(u =&gt; u.Age == 32) 时,它不会枚举,而只是创建引用原始的一个和一些附加的“哪里”过滤器?或者它是如何工作的?也许这应该是另一个问题...
    • 我无法回答它是如何工作的,但它对我来说很有意义(IEnumerableIQueryable 都推迟了执行)。这就是为什么我在测试之前怀疑我的答案的原因。
    • 据我了解,当对 IQueryable 变量进行 .Where() 或其他 Linq 调用时,表示表达式的对象树将添加到实现 IQueryable 的对象中。 IQueryable 实现了IEnumerable,所以同样的事情发生了。当您实际枚举表达式时,表达式树被评估/转换为 SQL 并传递给数据库服务器。
    【解决方案2】:

    这里有一个简单的解释:

    IEnumerable<User> usersEnumerable = db.UserSet;
    IQueryable<User> usersQueryable = db.UserSet;
    
    var users = /* one of usersEnumerable or usersQueryable */;
    
    var age32StartsWithG = users.Where(user => user.Age == 32)
                                .Where(user => user.Name.StartsWith("G");
    

    如果你使用usersEnumerable,当你开始对其进行枚举时,两个Wheres将依次运行;首先ObjectSet 将获取所有对象,这些对象将被过滤到年龄为 32 岁的对象,然后这些对象将被过滤到名称以 G 开头的对象。

    如果您使用usersQueryable,两个Wheres 将返回新对象,这些对象将累积选择条件,当您开始对其进行枚举时,它会将所有条件转换为查询。这会产生明显的差异。

    通常,您不必担心,因为您在声明变量时会说 var usersObjectSet users,这意味着 C# 会知道您有兴趣调用可用的最具体的方法在ObjectSet 上,IQueryable 查询运算符方法(WhereSelect、...)比IEnumerable 方法更具体。但是,如果您将对象传递给采用 IEnumerable 参数的方法,它们最终可能会调用错误的方法。

    您还可以通过使用AsEnumerable()AsQueryable() 方法来开始使用其他方法,从而利用这种方式对您有利。例如,var groupedPeople = users.Where(user =&gt; user.Age &gt; 15).AsEnumerable().GroupBy(user =&gt; user.Age); 将通过数据库查询拉取正确的用户,然后在本地对对象进行分组。

    正如其他人所说,值得重复的是,在您开始枚举序列之前什么都不会发生(使用foreach)。您现在应该明白为什么不能采用其他方式了:如果一次检索所有结果,您就无法构建查询以转换为更有效的查询(如 SQL 查询)。

    【讨论】:

      【解决方案3】:

      不同之处在于 IEnumerable 会执行多个过滤器,一次一个。例如,来自 100 个元素将通过第一次过滤器输出 20,然后将第二次过滤所需的 10。它将对数据库进行一次查询,但会下载不必要的数据。使用 IQueryable 将通过一个查询再次下载,但只有所需的 10 个项目。以下链接提供了一些关于这些查询如何工作的优秀示例: https://filteredcode.wordpress.com/2016/04/29/ienumerable-vs-iqueryable-part-2-practical-questions/

      【讨论】:

        【解决方案4】:

        您对 IQueryable 的看法是正确的。至于 IEnumerable,它会在分配 IEnumerable 用户后立即访问数据库。

        在您提供的示例中使用 Linq 扩展与语法之间没有真正的区别。有时其中一种会更方便(请参阅linq-extension-methods-vs-linq-syntax),但 IMO 更多的是关于个人喜好。

        【讨论】:

        • 所以当使用IEnumerable 时,它会立即访问数据库,它会请求用户表中的所有行或仅请求年龄设置为 32 的行?
        猜你喜欢
        • 2017-09-13
        • 1970-01-01
        • 1970-01-01
        • 2021-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-27
        相关资源
        最近更新 更多