IQueryable,List,IEnumerator 之间的区别? ?

我想知道 IQueryable,List,IEnumerator 之间的区别是什么,以及什么时候应该使用它们?

例如,当使用 Linq to SQL 时,我会这样做:

public List<User> GetUsers()
{
return db.User.where(/* some query here */).ToList();
}

现在我想知道是否应该使用 IQueryable。我不确定使用它比列表有什么好处。

57028 次浏览

IQueryable<T>: 抽象数据库访问,支持查询的延迟计算
List<T>: 一个条目集合。不支持延迟计算
IEnumerator<T>: 提供遍历和 IEnumerable<T>(IQueryable<T>List<T>都是)的能力

这段代码的问题很简单——它总是在调用查询时执行查询。如果要返回 db.User.Where(...)(这是一个 IQueryable<T>) ,则需要保留查询的计算,直到实际需要(迭代)。此外,如果该方法的用户需要指定进一步的谓词,那么这些谓词也将在数据库中执行,从而使其运行速度更快。

IQueryable<T>旨在允许查询提供程序(例如,像 LINQ to SQL 或实体框架这样的 ORM)使用查询中包含的表达式将请求转换为另一种格式。换句话说,LINQ-to-SQL 查看您正在使用的实体的属性以及正在进行的比较,并实际创建一个 SQL 语句来表达(希望)一个等价的请求。

IEnumerable<T>IQueryable<T>更通用(尽管 IQueryable<T>的所有实例都实现了 IEnumerable<T>) ,并且只定义了一个序列。但是,Enumerable类中有一些可用的扩展方法,它们在该接口上定义一些查询类型的运算符,并使用普通代码来计算这些条件。

List<T>只是一种输出格式,虽然它实现了 IEnumerable<T>,但与查询没有直接关系。

换句话说,当您使用 IQueryable<T>时,您正在定义一个 表情,它将被转换成其他内容。即使您正在编写代码,这些代码也不会得到 处决,它只能得到 检查并转换成其他东西,比如一个实际的 SQL 查询。因此,在这些表达式中只有某些事物是有效的。例如,您不能调用从这些表达式中定义的普通函数,因为 LINQ-to-SQL 不知道如何将调用转换为 SQL 语句。不幸的是,这些限制中的大多数只在运行时进行计算。

当您使用 IEnumerable<T>进行查询时,您使用的是 LINQ-to-Objects,这意味着您正在编写用于计算查询或转换结果的实际代码,因此,一般来说,对于您可以执行的操作没有限制。可以在这些表达式中自由调用其他函数。

使用 LINQtoSQL

与上面的区别齐头并进,同样重要的是要记住这在实践中是如何实现的。在 LINQtoSQL 中编写针对数据上下文类的查询时,它会生成一个 IQueryable<T>。无论您做什么,对抗 IQueryable<T>本身都会转换成 SQL,因此您的过滤和转换将在服务器上完成。无论您针对这个 作为一个 IEnumerable<T>做什么,都将在应用程序级别完成。有时这是可取的(例如,在需要使用客户端代码的情况下) ,但在许多情况下,这是无意的。

例如,如果我有一个具有表示 Customer表的 Customers属性的上下文,并且每个客户都有一个 CustomerId列,那么让我们看看执行这个查询的两种方法:

var query = (from c in db.Customers where c.CustomerId == 5 select c).First();

这将产生 SQL,用于查询数据库中的 Customer记录,CustomerId等于5:

select CustomerId, FirstName, LastName from Customer where CustomerId = 5

现在,如果我们使用 AsEnumerable()扩展方法将 Customers转换成 IEnumerable<Customer>会发生什么?

var query = (from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c).First();

这种简单的变化会产生严重的后果。因为我们将 Customers转换为 IEnumerable<Customer>,所以这将返回整个表并在客户端对其进行过滤(严格来说,这将返回表 直到它遇到一个符合标准的中的每一行,但重点是相同的)。

列表()

到目前为止,我们只讨论了 IQueryableIEnumerable。这是因为它们是相似的、互补的接口。在这两种情况下,都要定义一个 疑问; 也就是说,要定义 哪里来查找数据、应用 什么过滤器和返回 什么数据。这两个都是查询

query = from c in db.Customers where c.CustomerId == 5 select c;
query = from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c;

如前所述,第一个查询使用 IQueryable,第二个查询使用 IEnumerable。然而,在这两种情况下,这只是一个 疑问。定义查询实际上不会对数据源做任何事情。当代码开始在列表上迭代时,实际执行查询。这可能以多种方式发生: foreach循环、调用 ToList()等等。

查询在迭代时执行 第一个每个。如果在 query上两次调用 ToList(),最终会得到两个对象完全不同的列表。它们可能包含相同的数据,但它们是不同的引用。

评论后编辑

我只是想弄清楚什么时候在客户端完成任务,什么时候在服务器端完成任务。如果你引用一个 IQueryable<T>作为一个 IEnumerable<T>只有的查询完成了 之后它是一个 IEnumerable<T>将做客户端。例如,假设我有这个表和一个 LINQ-to-SQL 上下文:

Customer
-----------
CustomerId
FirstName
LastName

我首先构建一个基于 FirstName的查询,这将创建一个 IQueryable<Customer>:

var query = from c in db.Customers where c.FirstName.StartsWith("Ad") select c;

现在我将这个查询传递给一个函数,该函数接受 IEnumerable<Customer>并基于 LastName进行一些过滤:

public void DoStuff(IEnumerable<Customer> customers)
{
foreach(var cust in from c in customers where c.LastName.StartsWith("Ro"))
{
Console.WriteLine(cust.CustomerId);
}
}

我们在这里做了第二个查询,但它是在 IEnumerable<Customer>上完成的。这里将要发生的是第一个查询将被计算,运行这个 SQL:

select CustomerId, FirstName, LastName from Customer where FirstName like 'Ad%'

所以我们要把 FirstName"Ad"开头的所有人带回来。请注意,这里没有关于 LastName的内容。那是因为它被客户端过滤掉了。

一旦返回这些结果,程序就会遍历这些结果,并且只交付以 "Ro"开头的 LastName记录。这样做的缺点是我们带回了数据——即 LastName 没有"Ro"开头的所有行——这些 可以已经在服务器上被过滤掉了。

当需要某个实体的强类型集合时,请使用 iListList<item>

并且当您想要获得作为对象集合的哑数据时,使用 IqueryableIenumurator,它将作为松散类型集合返回,并且没有应用限制。

我宁愿使用 List<type>,因为在强类型集合中使用列表换行和强制转换是我的结果集。

此外,使用列表将使您能够添加,排序和转换层到数组,我枚举器或作为查询。