IQueryable,List,IEnumerator之间的区别?

我想知道IQueryable,List,IEnumerator和我应该使用每一个的区别是什么?

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

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

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


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> ,您正在定义一个表达式,并将其转换为其他内容。 即使您正在编写代码,该代码也不会执行,只会被检查并转换为其他内容,如实际的SQL查询。 正因为如此,这些表达式中只有某些东西是有效的。 例如,您不能调用您在这些表达式中定义的普通函数,因为LINQ-to-SQL不知道如何将您的调用转换为SQL语句。 不幸的是,这些限制中的大部分仅在运行时进行评估。

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

使用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记录。如下所示:

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 。 这是因为它们是类似的免费接口。 在这两种情况下,你都在定义一个查询; 也就是说,您正在定义在哪里查找数据,要应用哪些过滤器以及要返回哪些数据。 这两个都是查询

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-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"开头的行 - 可能已被服务器过滤掉。


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

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


当你想要某个实体的强类型集合时,使用iListList<item>

当你想将愚蠢的数据作为一个对象集合使用时,使用IqueryableIenumurator ,它将作为一个松散的类型集合返回,并且不会应用任何限制。

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

此外,使用列表可让您添加,排序并将图层转换为Array,Ienumurator或Queryable。

链接地址: http://www.djcxy.com/p/21251.html

上一篇: Differences between IQueryable, List, IEnumerator?

下一篇: What's the difference between IQueryable and IEnumerable