c# - IQueryable、List、IEnumerator 之间的区别?

标签 c# linq list ienumerable iqueryable

我想知道 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();
这种简单的改变带来了严重的后果。由于我们正在转弯CustomersIEnumerable<Customer> ,这将带回整个表并在客户端对其进行过滤(严格来说,这将带回表中的每一行,直到遇到符合条件的行,但重点是相同的)。
ToList()
到目前为止,我们只讨论了IQueryableIEnumerable .这是因为它们是相似的、互补的接口(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/

相关文章:

c# - ETW 格式字符串中的转义字符?

c# - 将数据从 Windows 服务器推送到 Raspberry Pi 上的自定义应用程序

linq - NHibernate 3 和 LINQ 支持吗?

javascript - Angular 如何将 JSON 数组值放入现有范围?

任何类型函数的 C++ 列表或 vector

c# - 记录C#程序的执行路径?

c# - 关于使用用户控件和母版页更改 ID 的问题

c# - 使用 LINQ 查询获取字段值

c# - SQL to Linq for inner and left join together

php - Laravel 在选择列表中获取更多数据