我想知道 IQueryable、List、IEnumerator 之间的区别是什么以及我应该何时使用它们?
例如,当使用 Linq to SQL 时,我会做这样的事情:
public List<User> GetUsers()
{
return db.User.where(/* some query here */).ToList();
}
现在我想知道是否应该使用 IQueryable 代替。我不确定在列表中使用它的优势。
最佳答案
IQueryable<T>
旨在允许查询提供程序(例如,像 LINQ to SQL 或 Entity Framework 这样的 ORM)使用查询中包含的表达式将请求转换为另一种格式。换句话说,LINQ-to-SQL 会查看您正在使用的实体的属性以及您正在进行的比较,并实际创建一个 SQL 语句来表达(希望如此)一个等效的请求。IEnumerable<T>
比 IQueryable<T>
更通用(尽管 IQueryable<T>
的所有实例都实现了 IEnumerable<T>
)并且只定义了一个序列。但是,在 Enumerable
中有可用的扩展方法。在该接口(interface)上定义一些查询类型运算符并使用普通代码来评估这些条件的类。List<T>
只是一种输出格式,虽然它实现了 IEnumerable<T>
, 与查询没有直接关系。
换句话说,当您使用 IQueryable<T>
时,您正在定义一个可以转换为其他内容的表达式。即使您正在编写代码,该代码也永远不会被执行,它只会被检查并转换为其他内容,例如实际的 SQL 查询。因此,在这些表达式中只有某些内容是有效的。例如,您不能调用从这些表达式中定义的普通函数,因为 LINQ-to-SQL 不知道如何将您的调用转换为 SQL 语句。不幸的是,大多数这些限制只在运行时评估。
当您使用 IEnumerable<T>
对于查询,您使用的是 LINQ-to-Objects,这意味着您正在编写用于评估查询或转换结果的实际代码,因此通常对您可以执行的操作没有限制。您可以从这些表达式中自由调用其他函数。
使用 LINQ to SQL
结合上述区别,记住这在实践中是如何运作的也很重要。当您针对 LINQ to SQL 中的数据上下文类编写查询时,它会生成 IQueryable<T>
.无论你对 IQueryable<T>
做什么本身将变成 SQL,因此您的过滤和转换将在服务器上完成。无论你做什么反对这个 IEnumerable<T>
,将在应用程序级别完成。有时这是可取的(例如,在您需要使用客户端代码的情况下),但在许多情况下这是无意的。
例如,如果我有一个带有 Customers
的上下文代表 Customer
的属性表,每个客户都有一个 CustomerId
列,让我们看看执行此查询的两种方法:
var query = (from c in db.Customers where c.CustomerId == 5 select c).First();
这将生成查询数据库 Customer
的 SQL。用 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
和 IEnumerable
.这是因为它们是相似的、互补的接口(interface)。在这两种情况下,您都在定义一个查询;也就是说,您要定义在何处查找数据、应用哪些过滤器以及返回哪些数据。这两个都是查询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()
, 等等。查询在第一次和每次迭代时执行。如果您拨打
ToList()
在 query
两次,您最终会得到两个具有完全不同对象的列表。它们可能包含相同的数据,但它们将是不同的引用。评论后编辑
我只想弄清楚什么时候在客户端完成和什么时候在服务器端完成之间的区别。如果您引用的是
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
的任何内容。 .那是因为它被客户端过滤掉了。一旦它带回这些结果,程序将迭代这些结果并只传递
LastName
的记录。以 "Ro"
开头.这样做的缺点是我们带回了数据——即所有 LastName
的行。不以 "Ro"
开头--可能已在服务器上过滤掉。
关于c# - IQueryable、List、IEnumerator 之间的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4844660/