IQueryable<T>和IEnumerable<T>之间的区别是什么?


另见IQueryable和IEnumerable之间的区别是什么,与这个问题重叠。


当前回答

下面提到的小测试可能会帮助你理解IQueryable<T>和IEnumerable<T>之间的一个区别。我从这篇文章中复制了这个答案,我试图对其他人的帖子进行更正

我在DB (DDL脚本)中创建了以下结构:

CREATE TABLE [dbo].[Employee]([PersonId] [int] NOT NULL PRIMARY KEY,[Salary] [int] NOT NULL)

下面是记录插入脚本(DML脚本):

INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(1, 20)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(2, 30)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(3, 40)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(4, 50)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(5, 60)
GO

现在我的目标是从数据库中的Employee表中获得前2条记录。在Visual Studio (VS)中创建了一个新的c#控制台应用程序。然后,我添加了一个ADO。NET实体数据模型XML (EDMX)项指向数据库中的Employee表。现在我可以开始编写LINQ查询了。

案例一:IQueryable路由代码

using (var efContext = new EfTestEntities())
{
    IQueryable<int> employees = from e in efContext.Employees  select e.Salary;
    employees = employees.Take(2);

    foreach (var item in employees)
    {
        Console.WriteLine(item);
    }
}

在运行这个程序之前,我已经在SQL Server实例上启动了一个SQL查询分析器会话。以下是执行概要:

触发的查询总数:1 查询文本: SELECT TOP (2) [c]。[薪金]AS[薪金]来自[dbo]。[雇员]AS [c]

我们可以看到IQueryable足够聪明,可以在数据库服务器端本身应用Top(2)子句。所以它只通过网络带来5条记录中的2条。在客户端不需要更多的内存过滤。

案例二:IEnumerable路由的代码

using (var efContext = new EfTestEntities())
{
    IEnumerable<int> employees = from e in efContext.Employees  select e.Salary;
    employees = employees.Take(2);

    foreach (var item in employees)
    {
        Console.WriteLine(item);
    }
}

本例执行摘要:

触发的查询总数:1 SQL分析器捕获的查询文本: 选择[Extent1]。[薪金]AS[薪金]来自[dbo]。[雇员]AS [Extent1]

现在需要注意的是,IEnumerable带来了Salary表中的所有5条记录,然后在客户端执行内存过滤以获得前2条记录。因此更多的数据(在本例中是3条额外的记录)通过网络传输,不必要地消耗了带宽。

其他回答

首先,IQueryable<T>扩展了IEnumerable<T>接口,所以任何你可以用“普通”IEnumerable<T>做的事情,你也可以用IQueryable<T>做。

IEnumerable<T>只有一个GetEnumerator()方法,该方法返回一个Enumerator<T>,为此您可以调用它的MoveNext()方法来遍历T的序列。

IQueryable<T>具有而IEnumerable<T>所没有的两个特殊属性——一个指向查询提供程序(例如,LINQ to SQL提供程序),另一个指向表示IQueryable<T>对象的查询表达式,作为一个运行时可遍历的抽象语法树,可以被给定的查询提供程序理解(在大多数情况下,你不能在不抛出异常的情况下将LINQ to SQL表达式提供给LINQ to Entities提供程序)。

表达式可以是对象本身的常量表达式,也可以是由查询操作符和操作数组成的更复杂的树。查询提供程序的IQueryProvider.Execute()或IQueryProvider.CreateQuery()方法通过传递给它的表达式被调用,然后分别返回一个查询结果或另一个IQueryable方法。

在现实生活中,如果您正在使用LINQ-to-SQL之类的ORM

如果您创建了IQueryable查询,那么该查询可能会转换为sql并在数据库服务器上运行 如果您创建了一个IEnumerable,那么在运行查询之前,所有行都将作为对象拉入内存。

在这两种情况下,如果你不调用ToList()或ToArray(),那么查询将在每次使用它时执行,所以,比如说,你有一个IQueryable<T>,你从它填充4个列表框,那么查询将对数据库运行4次。

同样,如果你扩展你的查询:

q.Where(x.name = "a").ToList()

然后使用IQueryable生成的SQL将包含" where name = " a ",但是使用IEnumerable会从数据库中拉回更多的角色,然后. net将完成x.name = " a "检查。

IEnumerable: IEnumerable最适合使用内存中的集合(或本地查询)。 IEnumerable不能在项目之间移动,它只是向前的集合。

这个IQueryable: IQueryable最适合远程数据源,如数据库或web服务(或远程查询)。 IQueryable是一个非常强大的特性,它支持各种有趣的延迟执行场景(比如分页和基于组合的查询)。

所以当你必须简单地遍历内存中的集合时,使用IEnumerable,如果你需要对集合做任何操作,如Dataset和其他数据源,使用IQueryable

主要的区别是IQueryable<T>的LINQ操作符接受表达式对象而不是委托,这意味着它接收的自定义查询逻辑,例如,谓词或值选择器,是表达式树的形式,而不是方法的委托。

IEnumerable<T>非常适合处理在内存中迭代的序列,但是 IQueryable<T>允许内存不足的事情,如远程数据源,如数据库或web服务。

查询执行:

查询的执行将在“进程中”执行,通常所需要的只是执行查询的每个部分的代码(作为代码)。 在将在进程外执行的地方,查询的逻辑必须用数据表示,以便LINQ提供程序可以将其转换为内存外执行的适当形式——无论是LDAP查询、SQL查询还是其他查询。

更多:

LINQ: IEnumerable<T> and IQueryable<T> c# 3.0和LINQ。 返回IEnumerable<T> vs IQueryable<T> 面向。net和c#开发人员的响应式编程——IEnumerable, IQueryable, IObservable和iqobservable的介绍 2008年:巴特·德·斯梅特的“年度最有趣的界面…可查询<T>”。

下面提到的小测试可能会帮助你理解IQueryable<T>和IEnumerable<T>之间的一个区别。我从这篇文章中复制了这个答案,我试图对其他人的帖子进行更正

我在DB (DDL脚本)中创建了以下结构:

CREATE TABLE [dbo].[Employee]([PersonId] [int] NOT NULL PRIMARY KEY,[Salary] [int] NOT NULL)

下面是记录插入脚本(DML脚本):

INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(1, 20)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(2, 30)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(3, 40)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(4, 50)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(5, 60)
GO

现在我的目标是从数据库中的Employee表中获得前2条记录。在Visual Studio (VS)中创建了一个新的c#控制台应用程序。然后,我添加了一个ADO。NET实体数据模型XML (EDMX)项指向数据库中的Employee表。现在我可以开始编写LINQ查询了。

案例一:IQueryable路由代码

using (var efContext = new EfTestEntities())
{
    IQueryable<int> employees = from e in efContext.Employees  select e.Salary;
    employees = employees.Take(2);

    foreach (var item in employees)
    {
        Console.WriteLine(item);
    }
}

在运行这个程序之前,我已经在SQL Server实例上启动了一个SQL查询分析器会话。以下是执行概要:

触发的查询总数:1 查询文本: SELECT TOP (2) [c]。[薪金]AS[薪金]来自[dbo]。[雇员]AS [c]

我们可以看到IQueryable足够聪明,可以在数据库服务器端本身应用Top(2)子句。所以它只通过网络带来5条记录中的2条。在客户端不需要更多的内存过滤。

案例二:IEnumerable路由的代码

using (var efContext = new EfTestEntities())
{
    IEnumerable<int> employees = from e in efContext.Employees  select e.Salary;
    employees = employees.Take(2);

    foreach (var item in employees)
    {
        Console.WriteLine(item);
    }
}

本例执行摘要:

触发的查询总数:1 SQL分析器捕获的查询文本: 选择[Extent1]。[薪金]AS[薪金]来自[dbo]。[雇员]AS [Extent1]

现在需要注意的是,IEnumerable带来了Salary表中的所有5条记录,然后在客户端执行内存过滤以获得前2条记录。因此更多的数据(在本例中是3条额外的记录)通过网络传输,不必要地消耗了带宽。