的IQueryable,列表,IEnumerator之间的区别?

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

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

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

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

chobo2 asked 2019-10-09T01:39:21Z
3个解决方案
104 votes

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

IEnumerable<Customer>LastName更通用(尽管IEnumerable<T>的所有实例都实现ToList()),并且仅定义了一个序列。 但是,Enumerable类中提供了一些扩展方法,这些扩展方法在该接口上定义了一些查询类型的运算符,并使用普通的代码来评估这些条件。

IEnumerable<Customer>只是一种输出格式,尽管它实现了LastName,但与查询没有直接关系。

换句话说,当您使用IEnumerable<Customer>时,您正在定义一个可转换为其他表达式的表达式。 即使您正在编写代码,该代码也永远不会执行,它只会被检查并转化为其他内容,例如实际的SQL查询。 因此,在这些表达式中只有某些事情有效。 例如,您不能调用从这些表达式中定义的普通函数,因为LINQ-to-SQL不知道如何将您的调用转换为SQL语句。 不幸的是,这些限制中的大多数仅在运行时评估。

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

使用LINQ to SQL

与上面的区别紧密结合,牢记这在实践中是如何工作也很重要。 当您在LINQ to SQL中针对数据上下文类编写查询时,它将生成IEnumerable<Customer>。对IEnumerable<Customer>本身执行的任何操作都将转换为SQL,因此将在服务器上进行过滤和转换。 对于IEnumerable<Customer>,无论您做什么,都将在应用程序级别完成。 有时这是理想的(例如,在需要使用客户端代码的情况下),但是在许多情况下,这是无意的。

例如,如果我有一个上下文,该上下文具有表示LastName表的IEnumerable<Customer>属性,并且每个客户都有IEnumerable<T>列,那么让我们看一下执行此查询的两种方法:

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

这将生成SQL,该查询将查询数据库中LastNameLastName记录,该记录等于5。类似于:

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

现在,如果使用IEnumerable<T>扩展方法将IEnumerable<Customer>转换为LastName,会发生什么情况?

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

这种简单的变化具有严重的后果。 由于我们将IEnumerable<Customer>转换为LastName,因此这将带回整个表并在客户端对其进行过滤(嗯,严格来说,这将带回表中的每一行,直到遇到符合条件的那一行为止,但要点 一样)。

ToList()

到目前为止,我们只讨论了IEnumerable<Customer>LastName。这是因为它们是相似的,互补的接口。 在这两种情况下,您都在定义查询。 也就是说,您正在定义要在哪里找到数据,要应用哪些过滤器以及要返回什么数据。 这两个都是查询

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;

就像我们已经讨论过的一样,第一个查询使用IEnumerable<Customer>,第二个查询使用LastName。但是,在两种情况下,这都只是一个查询。 定义查询实际上对数据源没有任何作用。 当代码开始遍历列表时,实际上会执行查询。 这可以通过多种方式发生。 IEnumerable<T>循环,调用ToList()等。

该查询将在第一次以及每次迭代时执行。 如果要在LastName上两次调用IEnumerable<Customer>,则最终将得到两个对象完全不同的列表。 它们可能包含相同的数据,但是它们将是不同的引用。

评论后编辑

我只想弄清楚何时在客户端完成事情和何时在服务器端完成事情之间的区别。 如果您将IEnumerable<Customer>引用为LastName,则只有IEnumerable<T>之后的查询才会在客户端进行。 例如,假设我有此表和LINQ-to-SQL上下文:

Customer
-----------
CustomerId
FirstName
LastName

我首先基于IEnumerable<Customer>构建查询。这将创建LastName

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%'

因此,我们将带回所有以"Ro"开头的LastName。请注意,这里没有任何内容LastName。这是因为它已被客户端过滤掉。

一旦返回这些结果,该程序将对结果进行迭代,并仅传递其LastName"Ro"开头的记录。其缺点是我们带回了数据,即,其LastName并非以"Ro"开头的所有行 -可能已经在服务器上过滤掉了。

Adam Robinson answered 2019-10-09T01:42:02Z
8 votes

db.User.Where(...):摘要数据库访问,支持对查询的惰性评估
IQueryable<T>:条目的集合。 不支持懒惰评估
IEnumerator<T>:提供迭代能力和IEnumerable<T>IQueryable<T>List<T>都是)

该代码的问题非常简单-它总是在被调用时执行查询。 如果要返回db.User.Where(...)(即IQueryable<T>),则将保留对查询的评估,直到实际需要它为止(重复)。 同样,如果该方法的用户需要指定其他谓词,则这些谓词也将在数据库中执行,这使其速度更快。

Femaref answered 2019-10-09T01:42:51Z
0 votes

当您需要某个实体的强类型集合时,请使用List<type>Ienumurator

如果想将哑数据作为对象集合使用,请使用List<type>Ienumurator,它将作为松散类型集合返回,并且不施加任何限制。

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

另外,使用列表将使您能够添加,排序和转换图层为Array,Ienumurator或可查询。

Mahmoud Sayed answered 2019-10-09T01:43:38Z
translate from https://stackoverflow.com:/questions/4844660/differences-between-iqueryable-list-ienumerator