由于看似相互矛盾的回复(主要是关于IEnumerable的),我想澄清一些事情。
(1) IQueryable扩展了IEnumerable接口。(您可以将IQueryable发送到期望IEnumerable的内容而不出错。)
(2) 当迭代结果集时,IQueryable和IEnumerable LINQ都尝试进行延迟加载。(请注意,每种类型的接口扩展方法中可以看到实现。)
换句话说,IEnumerable不仅仅是“内存中的”。IQueryables并不总是在数据库上执行。IEnumerable必须将东西加载到内存中(一旦检索到,可能会延迟),因为它没有抽象数据提供程序。IQueryables依赖于抽象提供程序(如LINQ-to-SQL),尽管这也可以是.NET内存提供程序。
示例用例
(a) 从EF上下文作为IQueryable检索记录列表。(没有记录在内存中。)
(b) 将 IQueryable
传递给一个模型为 IEnumerable
的视图。 (有效的。 IQueryable
扩展了 IEnumerable
.)
(c) 在视图中迭代访问数据集、子实体和属性。 (可能会引起异常!)
可能存在的问题
(1) IEnumerable
尝试惰性加载,而您的数据上下文已过期。由于提供程序不再可用,因此会引发异常。
(2) 启用了 Entity Framework 实体代理(默认),并且您尝试使用过期的数据上下文访问相关(virtual)对象。与(1)相同。
(3) 多活动结果集(MARS)。如果您在 foreach(var record in resultSet)
块中迭代访问 record.childEntity.childProperty
,同时尝试访问数据集和关系实体的惰性加载,您可能会遇到MARS。如果它未在连接字符串中启用,则会引发异常。
解决方案
- 我发现在连接字符串中启用MARS的工作不可靠。我建议您避免使用MARS,除非它被充分理解并明确需要。
通过调用resultList = resultSet.ToList()
执行查询并存储结果,这似乎是确保实体在内存中的最简单直接的方法。
在访问相关实体的情况下,您可能仍然需要数据上下文。要么如此,要么您可以禁用实体代理并从DbSet
显式Include
相关实体。
IEnumerable
而非IQueryable
的另一个原因是,并非所有的LINQ提供程序都支持所有的LINQ操作。只要你知道自己在做什么,就可以使用IQueryable
将尽可能多的查询推送到LINQ提供程序(如LINQ2SQL,EF,NHibernate,MongoDB等)。但是,如果让其他代码随意处理你的IQueryable
,最终会遇到麻烦,因为某个客户端代码使用了不受支持的操作。我赞同建议,在存储库或等效层之外不要泄露IQueryable
。 - Avish