一尘不染

IQueryable,List,IEnumerator之间的区别?

c#

我想知道IQueryable,List,IEnumerator和什么时候应该使用每个之间有什么区别?

例如,当使用Linq进行SQL时,我将执行以下操作:

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

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


阅读 503

收藏
2020-05-19

共1个答案

一尘不染

IQueryable<T>旨在允许查询提供程序(例如,诸如LINQ to SQL或Entity
Framework之类的ORM)使用查询中包含的表达式将请求转换为另一种格式。换句话说,LINQ-to-
SQL会查看您正在使用的实体的属性以及进行的比较,并实际上创建一个SQL语句来表示(希望)一个等效请求。

IEnumerable<T>IQueryable<T>(尽管IQueryable<T>Implement的所有实例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>,将在应用层来完成。有时这是理想的(例如,在需要使用客户端代码的情况下),但是在许多情况下,这是无意的。

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

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

这将产生SQL,该SQL向数据库查询等于5 的Customer记录CustomerId。类似:

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();

这种简单的变化具有严重的后果。由于我们要转换CustomersIEnumerable<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。但是,在两种情况下,这都只是一个
query
。定义查询实际上对数据源没有任何作用。当代码开始遍历列表时,实际上会执行查询。这可以通过多种方式发生。一个foreach循环,调用ToList()等。

该查询 在第一次每次
迭代时执行。如果你要调用ToList()query两倍,你最终会得到两个清单完全不同的对象。它们可能包含相同的数据,但是它们将是不同的引用。

评论后编辑

我只想弄清楚何时在客户端完成事情和何时在服务器端完成事情之间的区别。如果您将an引用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"凹口- 可能 已被过滤掉的服务器上。

2020-05-19