为啥我需要 ToList() 来避免已处理的上下文错误?
Posted
技术标签:
【中文标题】为啥我需要 ToList() 来避免已处理的上下文错误?【英文标题】:Why do I need a ToList() to avoid disposed context errors?为什么我需要 ToList() 来避免已处理的上下文错误? 【发布时间】:2015-02-15 02:33:21 【问题描述】:我正在编写一些代码来使用 EntityFrameWork 访问数据库。代码是:
public IEnumerable<Rows> GetRows(int id)
using (var context = new ApplicationDbContext())
var repository = new EntityFrameWorkRepository<int, RowEntity>(context);
//need a ToList() here to prevent disposed dbcontext errors
return repository.GetRowsFromDb(id).ToList();
GetRowsFromDb() 使用 LINQ 查询数据库并使用 id 过滤结果。
我最初在没有调用 ToList() 的情况下编写了上述方法,但是当我尝试访问返回的 IEnumerable 中的对象时,我会收到一个关于已处置 dbcontext 的异常。我不明白上面的代码是如何解决问题的,尽管它确实可以工作。我假设 ToList() 正在深度复制对象,这可能提供了与上下文/数据库所需的分离,但原始对象肯定应该可用吗?
【问题讨论】:
IEnumerables
是惰性的,因此上下文将在调用方法中获取任何结果之前被释放。 ToList
让事情变得急切。
搜索“LINQ 延迟执行”
随机相关:***.com/questions/27491762/…
【参考方案1】:
您需要调用 ToList
、ToArray
或其他枚举 EF 返回数据的方法的原因是 LINQ 中的查询执行是延迟:数据不会被处理,直到你明确地接受它。当您的方法返回获取查询数据的上下文时已关闭(您的 using
块会迅速处理这种情况),从而导致您看到的异常。
这样做是为了让代码不会花时间处理您不需要的数据。例如,您可以编写从客户端开始读取数据并在中间停止的代码。如果查询执行没有被延迟,您将花费时间和内存来获取查询的“尾部”,然后将其丢弃。延迟执行让您拥有控制权:您可以随时决定要保留哪些数据,或者根据您计划对数据执行的操作将整个集合存储到内存中。
【讨论】:
【参考方案2】:如果您不调用.ToList()
,则将在您的using
子句完成后评估枚举,因此数据上下文将在评估查询之前被释放。
IMO,您应该考虑让您的存储库处理此问题(通过调用 .ToList()
),否则此问题代表实施细节泄露。
【讨论】:
【参考方案3】:没有ToList()
,你只会返回枚举器而不是对象的实际集合。当您尝试访问集合时,将获取实际对象。但在这种情况下,您需要的是上下文和存储库,因为您从数据库中访问它们。但由于它已经超出了using
子句的范围,因此两者都被处置,因此例外。
【讨论】:
【参考方案4】:我假设 ToList() 正在深度复制对象
不完全是-在您致电ToList
之前,您所拥有的只是一个查询。在通过ToList
、ToArray
、etc
枚举或将其转换为具体集合之前,您不会得到结果。
确定原始对象应该可用吗?
不 - 它已被释放,这是您告诉系统该对象已完成其工作并且不再需要的方式。您仍然有未执行的查询这一事实不会使上下文保持可用状态。
【讨论】:
以上是关于为啥我需要 ToList() 来避免已处理的上下文错误?的主要内容,如果未能解决你的问题,请参考以下文章