返回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<T>。 如果您想传输一组对象来枚举,则返回IEnumerable。

想象一下IQueryable是什么——一个数据的“查询”(如果你想,你可以对它进行细化)。IEnumerable是一组对象(已经接收到或创建了),可以对其进行枚举。

其他回答

我想澄清一些事情,因为似乎相互矛盾的反应(主要围绕IEnumerable)。

IQueryable扩展了IEnumerable接口。(你可以发送一个IQueryable给一个期望IEnumerable而没有错误的对象。)

(2) IQueryable和IEnumerable LINQ在迭代结果集时都尝试延迟加载。(注意,可以在每种类型的接口扩展方法中看到实现。)

换句话说,IEnumerables不是独占的“内存中”。IQueryables并不总是在数据库上执行。IEnumerable必须将数据加载到内存中(一旦检索到,可能是惰性的),因为它没有抽象数据提供程序。IQueryables依赖于抽象提供者(如LINQ-to-SQL),尽管这也可以是. net内存中的提供者。

样例用例

(a)从EF上下文检索IQueryable记录列表。(内存中没有记录。)

(b)将IQueryable传递给模型为IEnumerable的视图。(有效。IQueryable扩展IEnumerable。)

(c)在视图中遍历并访问数据集的记录、子实体和属性。(可能会导致异常!)

可能的问题

IEnumerable尝试延迟加载,你的数据上下文过期了。由于提供程序不再可用而引发异常。

(2)实体框架实体代理已启用(默认),并且您尝试访问具有过期数据上下文的相关(虚拟)对象。同(1)。

(3)多活动结果集(MARS)。如果你在foreach(var record in resultSet)块中遍历IEnumerable,同时尝试访问record. childentity。childProperty,由于数据集和关系实体的惰性加载,您可能最终会得到MARS。如果在连接字符串中未启用该选项,则会导致异常。

解决方案

我发现在连接字符串中启用MARS工作不可靠。我建议你避免MARS,除非它被很好地理解和明确地渴望。

执行查询并通过调用resultList = resultSet.ToList()来存储结果,这似乎是确保实体位于内存中的最直接的方法。

在访问相关实体的情况下,可能仍然需要数据上下文。或者,您可以禁用实体代理,并显式地从DbSet中包含相关实体。

我最近遇到了IEnumerable v. IQueryable问题。所使用的算法首先执行IQueryable查询以获得一组结果。然后将这些元素传递给foreach循环,并将这些元素实例化为实体框架(Entity Framework, EF)类。然后在Linq to Entity查询的from子句中使用这个EF类,导致结果为IEnumerable。

我对EF和实体的Linq相当陌生,所以花了一段时间才弄清楚瓶颈是什么。使用MiniProfiling,我找到了查询,然后将所有单独的操作转换为单个IQueryable Linq for Entities查询。IEnumerable执行了15秒,IQueryable执行了0.5秒。其中涉及三个表,在阅读本文后,我相信IEnumerable查询实际上形成了三个表的交叉积并过滤了结果。

尝试使用IQueryables作为经验法则,分析您的工作,使您的更改可测量。

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

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

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

除了前两个非常好的答案(由driis和Jacob):

IEnumerable 接口在系统中。集合名称空间。

IEnumerable对象表示内存中的一组数据,并且只能根据这些数据向前移动。由IEnumerable对象表示的查询立即完整地执行,因此应用程序可以快速地接收到数据。

当执行查询时,IEnumerable加载所有数据,如果我们需要对其进行筛选,筛选本身是在客户端完成的。

可查询接口位于系统中。Linq命名空间。

IQueryable对象提供了对数据库的远程访问,并允许您以从头到尾的直接顺序或反向顺序在数据中导航。在创建查询的过程中,返回的对象是IQueryable,查询是优化的。因此,在执行过程中消耗的内存更少,网络带宽更少,但同时它的处理速度比返回IEnumerable对象的查询稍微慢一些。

选择什么?

如果您需要返回的整个数据集,那么最好使用IEnumerable,它提供了最大的速度。

如果你不需要整个返回的数据集,而只是一些过滤后的数据,那么最好使用IQueryable。

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

出于这个原因,你可以将你的变量定义为'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.