关于C#:iQuery、List和IEnumerator之间的差异?

Differences between IQueryable, List, IEnumerator?

我想知道iqueryable、list、ienumerator和什么时候应该使用它们之间的区别是什么?

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

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

现在我想知道我是否应该使用iqueryable。我不确定使用它比列表的优势。


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

IEnumerableIQueryable更通用(尽管IQueryable的所有实例都实现IEnumerable,并且只定义序列。但是,在Enumerable类中有一些扩展方法可用于定义该接口上的一些查询类型运算符,并使用普通代码来评估这些条件。好的。

List只是一种输出格式,在实现IEnumerable的同时,与查询没有直接关系。好的。

换言之,当您使用IQueryable时,您定义了一个可以转换成其他内容的表达式。即使您正在编写代码,该代码也不会被执行,它只会被检查并转换为其他代码,比如实际的SQL查询。因此,在这些表达式中只有某些内容是有效的。例如,不能从这些表达式中调用定义的普通函数,因为Linq to SQL不知道如何将调用转换为SQL语句。不幸的是,这些限制中的大多数只在运行时进行评估。好的。

当您使用IEnumerable进行查询时,您使用的是linq to对象,这意味着您正在编写用于评估查询或转换结果的实际代码,因此一般来说,您可以做什么没有限制。您可以从这些表达式中自由调用其他函数。好的。

使用Linq to SQL好的。

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

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

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

这将生成查询数据库中Customer记录的SQL,其中CustomerId等于5。类似:好的。

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

现在,如果使用AsEnumerable()扩展方法将Customers转换成IEnumerable会发生什么?好的。

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

这种简单的改变会产生严重的后果。由于我们将Customers转换为IEnumerable,这将使整个表返回,并在客户端对其进行筛选(严格来说,这将使表中的每一行返回,直到遇到符合条件的行,但要点是相同的)。好的。

托尔斯特()好的。

到目前为止,我们只讨论过IQueryableIEnumerable。这是因为它们是相似的、免费的接口。在这两种情况下,您都定义了一个查询;也就是说,您定义了查找数据的位置、要应用的过滤器以及要返回的数据。这两个都是查询好的。

1
2
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引用为IEnumerable,那么只有在它是IEnumerable之后进行的查询才会在客户端进行。例如,假设我有这个表和一个Linq to SQL上下文:好的。

1
2
3
4
5
Customer
-----------
CustomerId
FirstName
LastName

我首先构建一个基于FirstName的查询。这就产生了一个IQueryable:好的。

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

现在,我将该查询传递给一个接受IEnumerable的函数,并根据LastName进行一些筛选:好的。

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

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

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

所以我们要把所有的FirstName的人都从"Ad"开始。注意这里没有关于LastName的内容。这是因为它被过滤掉了客户端。好的。

一旦返回这些结果,程序将迭代这些结果,并只传递其LastName"Ro"开头的记录。这样做的缺点是,我们带回了数据——也就是说,所有LastName没有以"Ro"开头的行——这些数据可能已经在服务器上被过滤掉了。好的。好啊。


IQueryable:抽象数据库访问,支持对查询的延迟评估
List:条目的集合。不支持延迟评估
IEnumerator提供迭代和IEnumerable的能力(IQueryableList都是)

这段代码的问题很简单——它总是在被调用时执行查询。如果您要返回db.User.Where(...)(这是一个IQueryable),您将保留对查询的评估,直到实际需要它为止(重复)。另外,如果该方法的用户需要指定更多的谓词,那么这些谓词也将在数据库中执行,这将使其更快地执行。


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

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

我宁愿使用List,因为在强类型集合中使用了列表包装和强制转换我的结果集。

此外,使用列表将使您能够添加、排序和将层转换为数组、IEnumurator或可查询。