为啥我需要 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】:

您需要调用 ToListToArray 或其他枚举 EF 返回数据的方法的原因是 LINQ 中的查询执行是延迟:数据不会被处理,直到你明确地接受它。当您的方法返回获取查询数据的上下文时已关闭(您的 using 块会迅速处理这种情况),从而导致您看到的异常。

这样做是为了让代码不会花时间处理您不需要的数据。例如,您可以编写从客户端开始读取数据并在中间停止的代码。如果查询执行没有被延迟,您将花费时间和内存来获取查询的“尾部”,然后将其丢弃。延迟执行让您拥有控制权:您可以随时决定要保留哪些数据,或者根据您计划对数据执行的操作将整个集合存储到内存中。

【讨论】:

【参考方案2】:

如果您不调用.ToList(),则将在您的using 子句完成后评估枚举,因此数据上下文将在评估查询之前被释放。

IMO,您应该考虑让您的存储库处理此问题(通过调用 .ToList()),否则此问题代表实施细节泄露。

【讨论】:

【参考方案3】:

没有ToList(),你只会返回枚举器而不是对象的实际集合。当您尝试访问集合时,将获取实际对象。但在这种情况下,您需要的是上下文和存储库,因为您从数据库中访问它们。但由于它已经超出了using 子句的范围,因此两者都被处置,因此例外。

【讨论】:

【参考方案4】:

我假设 ToList() 正在深度复制对象

不完全是-在您致电ToList 之前,您所拥有的只是一个查询。在通过ToListToArrayetc 枚举或将其转换为具体集合之前,您不会得到结果

确定原始对象应该可用吗?

不 - 它已被释放,这是您告诉系统该对象已完成其工作并且不再需要的方式。您仍然有未执行的查询这一事实不会使上下文保持可用状态。

【讨论】:

以上是关于为啥我需要 ToList() 来避免已处理的上下文错误?的主要内容,如果未能解决你的问题,请参考以下文章

减少上下文切换

并发编程中,如何减少上下文切换

为啥 toList() 在 Dart 中创建一个 List<dynamic>?

如何避免堆碎片?

关闭已创建 STA COM 对象的线程时避免断开上下文警告

ZeroMQ 套接字 Recv() 抛出“上下文已终止”异常 - 为啥以及如何恢复?