为什么要使用AsQueryable()而不是List()?

62

我开始使用存储库模式来访问数据,Entity FrameworkLINQ作为非测试存储库实现的基础。大多数示例在调用返回N个记录而不是List<T>时返回AsQueryable()。这样做的优点是什么?


2
什么是非测试代码库? - Mattias Nordqvist
5个回答

106

AsQueryable只是创建一个查询,也就是获取列表所需的指令。您可以稍后对查询进行进一步更改,例如添加新的Where子句,这些子句会发送到数据库层。

AsList返回一个实际的列表,其中包含所有的项目在内存中。如果您向其添加新的Where子句,则不会获得数据库提供的快速过滤功能。相反,您将获取列表中的所有信息,然后在应用程序中过滤掉不需要的内容。

因此,基本上就是等到最后一刻才做出承诺。


8
同意,但请记住,在“快速过滤”之外,还有将所有信息通过网络发送的影响。 - eglasius
2
此外,这并不是唯一的方法,您可以接收要应用的过滤器而不是返回可过滤的内容。如果以后从使用数据库转移到其他东西,例如Web服务或其他任何东西,第一种方法更容易使用。 - eglasius
17
另外要注意的一点是:如果你返回一个IQueryable,你必须找到一种好的方法来确保 1.) 上下文在查询执行之前保持打开状态,2.) 并且上下文将被正确地处理。返回一个列表的优点是可以控制上下文在方法内的生命周期。哪种更适合取决于实际需求。 - Daniel Brückner
2
关于@eglasius的问题,你可以很容易地将谓词作为参数传递给存储库方法,例如public GetMyEntities<T> (Func<bool,T> pred)public GetMyEntities<MyEntity> (Func<bool,MyEntity> pred),然后将其附加到查询中,如return context.MyEntities.Where(pred).ToList();。你应该先检查它是否为空,但你明白我的意思。然后在调用时应用谓词,而不是之后:repo.GetMyEntities(x => x.ThatProperty == false)。这可能会使非Linq2Sql兼容的方法更有可能抛出异常,但每种方法都有权衡。 - JoeBrockhaus
1
@JoeBrockhaus 如果你使用 GetMyEntities<T> (Expression<Func<<bool,T>> pred) 而不是 GetMyEntities<T>(Func<bool,T> pred) 会怎样呢? - Jonathan Allen

26

返回 IQueryable<T> 有一个优点,就是只有在你真正开始枚举结果并组合查询时才执行,你可以使用其他查询来组成查询,并仍然获得服务器端的执行。

问题在于,你无法控制此方法中数据库上下文的生命周期 - 你需要一个打开的上下文,并确保它保持打开状态直到查询被执行。然后你必须确保上下文将被处理。如果将结果作为 List<T>T[] 或类似的东西返回,你就会失去延迟执行和组合查询的服务器端执行,但你会获得对数据库上下文生命周期的控制。

当然,最适合的取决于实际要求。这是另一个没有单一答案的问题。


13

AsQueryableIEnumerable<T>的扩展方法,可以做两件事情:

  • 如果IEnumerable<T>实现了IQueryable<T>,则仅进行强制转换,不做任何操作。
  • 否则,创建一个“虚拟”的IEnumerable<T>EnumerableQuery<T>),该虚拟实现每个方法都编译lambda并调用Enumerable扩展方法。

因此,在大多数情况下,使用AsQueryable是无用的,除非您被迫将IQueryable传递给方法,而您只有IEnumerable,这是一种hack的方法。

注意:AsQueryable是hack方法,但IQueryable当然不是!


3
AsQueryable并不是无用的,因为你所描述的“被强制传递为IQueryable”的用例是有效的,它允许你使用IQueryable所提供的功能,而这比IEnumerable要多。我不认为它是一种hack,它有其用处,例如在这里:http://wcf.codeplex.com/wikipage?title=Getting%20started:%20Building%20a%20simple%20web%20api。但我猜你想说的是,它不能用于深入影响Enumerable数据的生成方式(例如优化查询),它只能作用于已经创建的内容之上。 - Evgeniy Berezovsky

4

返回IQueryable<T>将推迟查询的执行,直到实际使用其结果。 在此之前,您还可以在IQueryable<T>上执行其他数据库查询操作;而在List上,您通常只能执行效率较低的内存操作。


他的答案在我写我的时候提交了。 :) - dahlbyk

-1
  • IQueryable:这种类型的延迟执行方式被称为懒加载,所以查询会在需要时评估并访问到数据库。如果我们添加任何其他子句,它将使用带有过滤器的所需查询访问Db。
  • IEnumerable:是急切加载,因此记录首先将被加载到内存中,并且然后执行操作。

因此,根据用例,例如在大量记录上进行分页时,我们应该使用 IQueryable<T>,如果是短操作并且不会创建巨大的内存存储,则使用 IEnumerable


首先:急切和懒惰与评估无关。其次:急切加载并不意味着记录将首先在内存操作中加载,然后再执行操作。每个数据在操作之前都必须加载到内存中。 - Sebastian Xawery Wiśniowiecki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接