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的Customer
logging。就像:
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()
直到现在,我们只讨论了IQueryable
和IEnumerable
。 这是因为他们是相似的,免费的接口。 在这两种情况下,你都在定义一个查询 ; 也就是说,您正在定义在哪里查找数据,要应用哪些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集合时,使用iList
或List<item>
。
当你想把哑数据作为一个对象集合来使用Iqueryable
和Ienumurator
时,它将作为一个松散的types集合返回,并且没有任何限制。
我宁愿使用List<type>
因为使用列表包装并强制types集合我的结果集。
此外,使用列表将使您能够添加,sorting并将图层转换为Array,Ienumurator或Queryable。