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


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


当前回答

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

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

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

其他回答

Ienumerable:当我们想处理进程内内存时,即没有数据连接 可查询:何时处理SQL server,即数据连接 Ilist:添加对象、删除对象等操作

这是youtube上的一个很好的视频,它演示了这些界面的区别,值得一看。

下面是一个很长的描述性答案。

首先要记住的是IQueryable接口继承自IEnumerable,所以无论IEnumerable能做什么,IQueryable也能做什么。

有很多不同之处,但让我们来讨论一个最大的不同,它造成了最大的不同。IEnumerable接口是有用的,当你的集合是使用LINQ或实体框架加载,你想对集合应用过滤器。

考虑下面使用IEnumerable实体框架的简单代码。它使用Where过滤器获取EmpId为2的记录。

EmpEntities ent = new EmpEntities();
IEnumerable<Employee> emp = ent.Employees; 
IEnumerable<Employee> temp = emp.Where(x => x.Empid == 2).ToList<Employee>();

过滤器在IEnumerable代码所在的客户端执行。换句话说,从数据库中获取所有数据,然后在客户端进行扫描并获得EmpId为2的记录。

但是现在看下面的代码,我们把IEnumerable改成了IQueryable。它在服务器端创建一个SQL查询,只将必要的数据发送到客户端。

EmpEntities ent = new EmpEntities();
IQueryable<Employee> emp = ent.Employees;
IQueryable<Employee> temp =  emp.Where(x => x.Empid == 2).ToList<Employee>();

IQueryable和IEnumerable的区别在于过滤器逻辑执行的位置。一个在客户端执行,另一个在数据库上执行。

因此,如果你只使用内存中的数据收集IEnumerable是一个很好的选择,但如果你想查询与数据库连接的数据收集IQueryable是一个更好的选择,因为它减少了网络流量,并使用SQL语言的力量。

以下是我在类似的帖子(关于这个话题)上所写的内容。(不,我通常不引用自己的话,但这些都是非常好的文章。)

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

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

Expression trees are a very important construct in C# and on the .NET platform. (They are important in general, but C# makes them very useful.) To better understand the difference, I recommend reading about the differences between expressions and statements in the official C# 5.0 specification here. For advanced theoretical concepts that branch into lambda calculus, expressions enable support for methods as first-class objects. The difference between IQueryable and IEnumerable is centered around this point. IQueryable builds expression trees whereas IEnumerable does not, at least not in general terms for those of us who don't work in the secret labs of Microsoft.

下面是另一篇非常有用的文章,从推和拉的角度详细介绍了两者的区别。(通过“push”和“。“拉”,我指的是数据流的方向。用于。net和c#的响应式编程技术

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

在现实生活中,如果您正在使用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 "检查。

下面提到的小测试可能会帮助你理解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条额外的记录)通过网络传输,不必要地消耗了带宽。