IQueryable,List,IEnumerator之间的区别?

我想知道什么是IQueryable,列表,IEnumerator和什么时候我应该使用每一个不同?

例如,当使用LINQ到SQL我会做这样的事情

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

现在我想知道是否应该使用IQueryable,但我不确定在列表中使用它的优点。

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

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

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

换句话说,当你使用IQueryable<T> ,你正在定义和expression的东西被翻译成别的东西。 即使您正在编写代码,该代码也不会被执行 ,只会被检查并转化为其他内容,如实际的SQL查询。 正因为如此,这些expression式中只有某些东西是有效的。 例如,你不能调用你在这些expression式中定义的普通函数,因为LINQ-to-SQL不知道如何把你的调用转换成SQL语句。 大部分这些限制只在运行时才被评估,不幸的是。

当您使用IEnumerable<T>进行查询时,您正在使用LINQ-to-Objects,这意味着您正在编写用于评估查询或转换结果的实际代码,所以通常没有任何限制你可以做什么。 您可以自由地从这些expression式中调用其他函数。

用LINQ to SQL

要与上述区别结合起来,牢记在实践中这是如何实现的也是很重要的。 当您在LINQ to SQL中针对数据上下文类编写查询时,它会生成一个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查询数据库的CustomerId等于5的Customerlogging。就像:

 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> ,这将把整个表格带回来,并在客户端进行过滤(严格来说,这将带回表中的每一行, 直到遇到一个符合关键字的行 ,但重点是相同的)。

ToList()

直到现在,我们只讨论了IQueryableIEnumerable 。 这是因为他们是相似的,免费的接口。 在这两种情况下,你都在定义一个查询 ; 也就是说,您正在定义在哪里查找数据,要应用哪些filter以及要返回哪些数据。 这两个都是查询

 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到SQL的上下文:

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

我首先构造一个基于FirstName的查询。 这将创build一个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 。 那是因为它被客户端过滤掉了。

一旦它带回这些结果,程序将迭代结果并仅传递LastName"Ro"开头的logging。 这样做的缺点是我们带回了数据,也就是所有的LastName 不是"Ro"开始的行 – 可能已经被服务器过滤了。

IQueryable<T> :抽象数据库访问,支持查询的懒惰评估
List<T> :条目的集合。 懒惰的评价不支持
IEnumerator<T> :提供迭代和IEnumerable<T>IQueryable<T>List<T>都是)的能力,

该代码的问题非常简单 – 它总是在调用时执行查询。 如果你要返回db.User.Where(...)而不是(这是一个IQueryable<T> ),你可以持续对查询进行评估,直到实际需要(迭代)为止。 另外,如果该方法的用户需要指定更多的谓词,那么这些谓词也将在数据库中执行,这使得它更快。

当你需要某个实体的强types集合时,使用iListList<item>

当你想把哑数据作为一个对象集合来使用IqueryableIenumurator时,它将作为一个松散的types集合返回,并且没有任何限制。

我宁愿使用List<type>因为使用列表包装并强制types集合我的结果集。

此外,使用列表将使您能够添加,sorting并将图层转换为Array,Ienumurator或Queryable。