我正在用EF编写ASP.NET MVC应用程序。关于我的方法是否合理,我有一些问题。 注意:我简化了这个问题的基本结构,实际上一切都松散了。
让我们假设一个允许用户更改复杂域模型属性的视图。数据来自数据库(通过EF),最后必须再次写回。
根据我的理解,每个请求都会导致调用一个新的控制器实例。因此,我正在使用' Dispose Pattern'描述here as Option 2,它确保每个请求都有一个新的DbContext:
public class MyController : Controller
{
private MyContext repo = new MyContext();
protected override void Dispose(bool disposing)
{
this.repo.Dispose();
base.Dispose(disposing);
}
//more code...
}
现在控制器上有一个public ActionResult Edit(elementId)
方法,它将从数据库中获取一个元素并为其显示一个编辑器。完成此请求后,对Dbcontext的任何引用都将消失,但我仍然可以访问从数据库中提取的实体对象我将其存储在我的会话中。
稍后用户按下'保存'视图上的按钮。对Controller的Save-method的请求再次创建了一个Controller的新实例,因此创建了一个新的DbContext。检索存储在我的会话中的Entity对象,并根据用户的输入修改其属性。要将新状态保存到数据库,我必须将其附加到新上下文:
public void Save()
{
this.repo.MyTable.Attach(myEntity);
myEntity.Name = "New Name";
this.repo.SaveChanges();
}
只有在处理了myEntity
的原始DbContext的旧控制器后才能使用此功能,否则'附加'会失败(无法将实体附加到两个上下文)。 我担心我是否可以依赖此处处置的旧DbContext。
另外:我知道使用IoC框架是另一种选择。这将如何适用于我的情况,会有什么好处?
答案 0 :(得分:4)
我认为您为了“简化”问题而编写了太多代码,因此实际上掩盖了一些重要问题。但是,根据您发布的Save
方法,我可以很好地猜测您的问题。你的回购最有可能创建它自己的上下文,这是一个非常大的禁忌。
为了回答您的整体问题,此处实现IDisposable
的基本原理与其他任何地方相同:任何拥有实现IDisposable
的依赖项的类也应实现{{ 1}}。在这里,您的控制器实例化IDisposable
,因此它应该在完成时处置MyContext
。就这么简单。
但是,如果引入依赖项注入并将上下文注入控制器,则控制器不再拥有上下文。相反,DI容器将拥有它。因此,您的控制器应该不处理上下文,因为它不拥有它。
而且,你应该在这里使用依赖注入。您的上下文应该注入您的存储库,然后您的存储库应该注入您的控制器。这确保了只有一个上下文实例,并且您不会遇到类似于您现在遇到的问题,其中EF抱怨该实体属于不同的上下文。
最后,我只想鹦鹉@SteveGreene说,绝对没有理由将你的实体存储在会话中,事实上你应该没有多少理由,其中最重要的是它会挫败任何努力从In Proc会话转移到更可靠的会话存储。一旦你使用StateServer,SQL Server,Redis等等,你在会话中放置的任何内容都必须可序列化,并且实体通常很难(如果不是不可能)序列化,因为它们通常具有许多与其他实体的关系,以及与这些实体的循环关系。