由于看似矛盾的响应(主要围绕 IEnumerable),我想澄清一些事情。
(1) 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) 迭代并从视图访问数据集的记录、子实体和属性。 (可能会导致异常!)
可能的问题
(1) IEnumerable 尝试延迟加载并且您的数据上下文已过期。由于提供程序不再可用而引发异常。
(2) Entity Framework 实体代理已启用(默认),并且您尝试访问具有过期数据上下文的相关(虚拟)对象。同(1)。
(3) 多个活动结果集 (MARS)。如果您在 foreach( var record in resultSet ) 块中迭代 IEnumerable 并同时尝试访问 record.childEntity.childProperty,则可能由于数据集和关系实体的延迟加载而最终得到 MARS。如果您的连接字符串中未启用,这将导致异常。
解决方案
- 我发现在连接字符串中启用 MARS 不可靠。我建议您避免使用 MARS,除非它被充分理解并明确需要。
通过调用resultList = resultSet.ToList() 执行查询并存储结果这似乎是确保您的实体在内存中的最直接的方法。
在您访问相关实体的情况下,您可能仍需要数据上下文。要么这样,要么您可以禁用实体代理并从您的DbSet 明确地Include 相关实体。