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>
,你正在定义一个表达被翻译成其他东西。即使你正在编写代码,该代码也永远不会被理解executed,它只得到检查过的并变成其他东西,比如实际的 SQL 查询。因此,这些表达式中只有某些内容是有效的。例如,您无法调用在这些表达式中定义的普通函数,因为 LINQ-to-SQL 不知道如何将您的调用转换为 SQL 语句。不幸的是,大多数这些限制仅在运行时评估。
当你使用IEnumerable<T>
对于查询,您使用 LINQ-to-Objects,这意味着您正在编写用于评估查询或转换结果的实际代码,因此一般来说,您可以执行的操作没有限制。您可以从这些表达式中自由调用其他函数。
使用 LINQ to SQL
除了上述区别之外,记住这在实践中如何发挥作用也很重要。当您在 LINQ to SQL 中针对数据上下文类编写查询时,它会生成一个IQueryable<T>
。不管你做什么反对这IQueryable<T>
itself将被转换为 SQL,因此您的过滤和转换将在服务器上完成。无论你做什么反对这个as an IEnumerable<T>
,将在应用程序级别完成。有时这是可取的(例如,在您需要使用客户端代码的情况下),但在许多情况下这是无意的。
例如,如果我有一个上下文Customers
代表一个属性Customer
表,每个顾客都有一个CustomerId
列,让我们看看执行此查询的两种方法:
var query = (from c in db.Customers where c.CustomerId == 5 select c).First();
这将生成查询数据库的 SQLCustomer
记录与CustomerId
等于 5。类似:
select CustomerId, FirstName, LastName from Customer where CustomerId = 5
现在,如果我们转向会发生什么Customers
进入一个IEnumerable<Customer>
通过使用AsEnumerable()
扩展方法?
var query = (from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c).First();
这个简单的改变会带来严重的后果。既然我们要转身Customers
进入一个IEnumerable<Customer>
,这将带回整个表并在客户端对其进行过滤(严格来说,这将带回表中的每一行直到遇到符合条件的,但要点是一样的)。
ToList()
到目前为止,我们只讨论了IQueryable
and IEnumerable
。这是因为它们是相似的、互补的接口。在这两种情况下,您都定义了query;也就是说,你正在定义where查找数据,what要应用的过滤器,以及what要返回的数据。这两个都是查询
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
。然而,在这两种情况下,这都只是一个query。定义查询实际上不会对数据源执行任何操作。当代码开始迭代列表时,查询实际上被执行。这可以通过多种方式发生; Aforeach
循环,调用ToList()
, etc.
查询已执行首先 and every迭代的时间。如果你打电话ToList()
on query
两次,您最终会得到两个具有完全不同对象的列表。它们可能包含相同的数据,但它们将是不同的引用。
评论后编辑
我只是想清楚什么时候在客户端完成任务和什么时候在服务器端完成任务之间的区别。如果您引用的是IQueryable<T>
as an IEnumerable<T>
, only查询完成after这是一个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
。那是因为它被客户端过滤掉了。
一旦返回这些结果,程序将迭代结果并仅提供其记录LastName
以。。开始"Ro"
。这样做的缺点是我们带回了数据——即所有行LastName
doesn't从...开始"Ro"
--that could已在服务器上被过滤掉。