返回IQueryable<T>和IEnumerable<T>之间的区别是什么,什么时候应该优先于另一个?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

两者都将被延迟执行,何时应该优先于其中一个?


当前回答

我们可以用同样的方法来使用它们,它们只是在性能上有所不同。

IQueryable仅以有效的方式对数据库执行。这意味着它创建了一个完整的选择查询,并且只获取相关的记录。

例如,我们想以名字以“Nimal”开头的前10名客户为例。在这种情况下,将生成select查询,从客户中选择名称为“Nimal%”的前10 *。

但是如果我们使用IEnumerable,查询就像select * from Customer,其中名称像' Nimal% ',前十位将在c#编码级别过滤(它从数据库中获取所有客户记录并将它们传递给c#)。

其他回答

“IEnumerable”和“IQueryable”之间的主要区别在于在哪里执行筛选逻辑。一个在客户端(内存中)执行,另一个在数据库中执行。

例如,我们可以考虑一个例子,我们的数据库中有一个用户的10,000条记录,假设只有900个是活动用户,所以在这种情况下,如果我们使用“IEnumerable”,那么首先它将所有10,000条记录加载到内存中,然后应用IsActive筛选器,最终返回900个活动用户。

而另一方面,在同样的情况下,如果我们使用“IQueryable”,它将直接应用数据库上的IsActive过滤器,直接从那里将返回900个活跃用户。

除了上面的,有趣的是,如果你使用IQueryable而不是IEnumerable,你可以得到异常:

如果products是一个IEnumerable,下面的工作很好:

products.Skip(-4);

然而,如果products是一个IQueryable对象,并且它试图从DB表中访问记录,那么你会得到这个错误:

offset子句中指定的偏移量不能为负。

这是因为构造了以下查询:

SELECT [p].[ProductId]
FROM [Products] AS [p]
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS

OFFSET不能为负值。

一般来说,您希望保留查询的原始静态类型,直到有必要。

出于这个原因,你可以将你的变量定义为'var',而不是IQueryable<>或IEnumerable<>,你会知道你没有改变类型。

如果您开始使用IQueryable<>,通常希望将其保留为IQueryable<>,直到有一些令人信服的理由更改它。这样做的原因是您希望为查询处理器提供尽可能多的信息。例如,如果你只打算使用10个结果(你已经调用Take(10)),那么你想让SQL Server知道这一点,这样它就可以优化它的查询计划,只向你发送你将使用的数据。

A compelling reason to change the type from IQueryable<> to IEnumerable<> might be that you are calling some extension function that the implementation of IQueryable<> in your particular object either cannot handle or handles inefficiently. In that case, you might wish to convert the type to IEnumerable<> (by assigning to a variable of type IEnumerable<> or by using the AsEnumerable extension method for example) so that the extension functions you call end up being the ones in the Enumerable class instead of the Queryable class.

是的,两者都会让你延期执行。

区别在于IQueryable<T>是允许LINQ-to- sql (LINQ. sql)的接口。对任何事情)去工作。因此,如果在IQueryable<T>上进一步细化查询,该查询将在数据库中执行(如果可能的话)。

对于IEnumerable<T>情况,它将是LINQ-to-object,这意味着所有与原始查询匹配的对象都必须从数据库加载到内存中。

在代码:

IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

该代码将只对选择的黄金客户执行SQL。另一方面,下面的代码将在数据库中执行原始查询,然后在内存中过滤出非黄金客户:

IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

这是一个非常重要的区别,在许多情况下,使用IQueryable<T>可以避免从数据库返回太多行。另一个主要的例子是分页:如果你在IQueryable上使用Take和Skip,你将只得到请求的行数;在IEnumerable< t>上这样做会导致你所有的行都被载入内存。

都能让你延期执行,没错。

至于哪个是首选的,这取决于您的底层数据源是什么。

返回一个IEnumerable将自动强制运行时使用LINQ to Objects来查询您的集合。

返回一个IQueryable(顺便说一下,它实现了IEnumerable)提供了额外的功能,可以将您的查询转换为可能在底层源上执行得更好的查询(LINQ to SQL, LINQ to XML,等等)。