为什么“Dispose”工作,而不是“使用(var db = new DataContext())”?

我正在创建一个由主题组成的论坛,这些主题由消息组成。

当我尝试在我的控制器中实现主题视图时:

public ActionResult Topic(int id) //Topic Id { using (var db = new DataContext()) { var topic = db.Topics.Include("Messages").Include("Messages.CreatedBy").Include("CreatedBy").FirstOrDefault(x => x.Id == id); //include the messages for each topic, and when they were created so that the last message can be displayed on the topic page return topic != null ? View(topic) : View(); } } 

我在尝试查看主题页面时收到此错误:

ObjectDisposedException未被用户代码处理

ObjectContext实例已被释放,不能再用于需要连接的操作。

该错误似乎并不特定于某一行,因为当我删除违规行时,相同的错误会在之前出现。

我用以下方法解决了这个问题:

 DataContext db = new DataContext(); 

在控制器的开头和:

 protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); } 

最后(并using

虽然这很有效,但我很好奇为什么“使用”不起作用,我真的很高兴在整个控制器中打开连接,并在最后手动处理它。

您的任何实体是否启用了延迟加载? 看起来有些查询在您的视图中执行,但是您在执行之前处理了上下文(因此错误说它已经被处理掉了)。 如果将处理放在控制器Dispose方法中,则将在处理控制器和上下文之前执行视图。

我建议安装Glimpse.Mvc5和Glimpse.EF6软件包。 配置glimpse后,您可以看到在页面中执行的每个查询。 您可能会惊讶地看到一些您不想要执行的其他查询。 这就是我不建议您直接在视图中使用实体的原因。

这种情况正在发生,因为LINQ实体通常是代理对象。 如果您有类似MyEntity.ChildEntities ,则在执行代码之前不会执行基础SQL查询来获取这些对象。 如果您在视图中访问它们,则视图在动作方法返回之后才会被绑定,此时DbContext已经被释放。

生命周期看起来像这样:

  1. 呼叫动作方法
  2. 获取topic的外部查询运行,但视图中触发更多SQL查询的任何其他访问器尚未执行。
  3. 我们现在已经离开了action方法,所以using刚刚处理过的DbContext。
  4. MVC框架将模型绑定到视图,该视图触发实际执行任何剩余查询并因DbContext被释放而失败。
  5. 请求生命周期即将结束,因此控制器处理完毕。

这是一个关于实体延迟加载的好资源。

这是Lazy Loading的一个问题,所以感谢@Dismissile正确的方向

一旦我使用’.Include’来加载Topic的每个虚拟属性,它运行正常:

 var topic = db.Topics.Include("Messages").Include("Messages.CreatedBy").Include("CreatedBy").Include("Forum").Include("DeletedBy").FirstOrDefault(x => x.Id == id); 

对不起,我想,任何人都不可能知道我宣称哪些属性是虚拟的。

顺便说一下,它在我在调试模式下检查属性之前有效,因为必须加载每个属性才能检查它

您无法使用上下文返回视图或其他内容,因为这会导致在此处置上下文时出现问题。

在你的情况下使用using语句的正确方法是

 public ActionResult Topic(int id) //Topic Id { Topic topic = null; // topic is your POCO using (var db = new DataContext()) { topic = db.Topics.Include("Messages").Include("Messages.CreatedBy").Include("CreatedBy").FirstOrDefault(x => x.Id == id); } return topic != null ? View(topic) : View(); } 

如果你在使用声明中也不做响应重定向..

请参阅此处: 使用MVC中的using语句处理数据库上下文