【问题标题】:ASP.NET MVC with EF and the Dispose Pattern带有 EF 和 Dispose 模式的 ASP.NET MVC
【发布时间】:2017-03-30 16:01:53
【问题描述】:

我正在使用 EF 编写一个 ASP.NET MVC 应用程序。我对我的方法是否合理有一些疑问。 注意:我为这个问题简化了我的基本结构,实际上一切都更松散地耦合。

让我们假设一个视图允许用户更改复杂域模型的属性。数据来自数据库(通过 EF),最后必须重新写回。

据我了解,每个请求都会调用一个新的控制器实例。因此,我使用here as Option 2 描述的“Dispose Pattern”,它确保每个请求都有一个新的 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 方法的请求再次创建了一个新的 Controller 实例,因此创建了一个新的 DbContext。检索存储在我的会话中的实体对象,并根据用户的输入修改其属性。要将新状态保存到数据库中,我必须将其附加到新上下文中:

public void Save()
{
    this.repo.MyTable.Attach(myEntity);
    myEntity.Name = "New Name";
    this.repo.SaveChanges();
}

这只能在具有myEntity 原始 DbContext 的旧控制器被释放后才能工作,否则“附加”将失败(无法将实体附加到两个上下文)。 我担心我是否可以依赖在此处处理的旧 DbContext。

另外:我知道使用 IoC 框架是一种替代方案。这将如何适用于我的情况,有什么好处?

【问题讨论】:

  • 您的link 会列出来。不确定将获取的实体存储在会话中是什么意思 - 无需将其传递给您的视图。最好使用ViewModels
  • 最好保持 DbContext 实例的打开时间尽可能短,仅在确实需要时实例化 DbContext 对象,并在不再需要为特定请求与数据库交互时立即关闭它.从架构的角度来看,我建议您将 IoC 与将处理您的 Web 请求的服务层一起应用,从而使您的控制器“无脂肪”。
  • 尝试探索 Unity.Mvc5。这是一个非常易于使用的 IoC 容器,并且最适合 MVC。它为每个请求提供对象生命周期管理,非常适合您的要求。如果您使用它,您将不必担心处理 DbContext。
  • 每个人都有自己的; DI 容器通常是非常个人的选择。但是,我不知道 Unity 是 MVC 支持的 最佳 容器的说法是否有很多依据。 Ninject非常与 MVC、Web Api 等配合得很好,使用起来同样简单。
  • @Steve Greene:我可能需要澄清“将实体存储在会话中”的含义。我将我的 ViewModel 存储在会话中,该会话还知道我正在处理的数据库实体的 ID 以及其他一些数据。视图模型会根据用户输入进行相应更改。除了将它存储在会话中之外,我还必须在请求到达控制器时获取对我的 ViewModel 的引用(例如最终调用的 Save-Method 以及在用户与视图交互期间调用的其他一些方法)。跨度>

标签: c# asp.net asp.net-mvc entity-framework


【解决方案1】:

我认为您为了“简化”问题而修改了太多代码,结果实际上掩盖了一些重要问题。但是,根据您发布的Save 方法,我可以很好地猜出您的问题。您的 repo 很可能会创建自己的上下文,这是一个非常大的禁忌。

为了回答您的总体问题,此处实现IDisposable 的基本原理与其他任何地方相同:拥有 实现IDisposable 的依赖项的任何类也应该实现IDisposable。在这里,您的控制器实例化了MyContext,因此它应该在完成后处理MyContext。就这么简单。

但是,如果您引入依赖注入,并将上下文注入控制器,则控制器不再拥有上下文。相反,DI 容器将拥有它。因此,您的控制器应该处置上下文,因为它不拥有它。

而且,你应该在这里使用依赖注入。您的上下文应该被注入到您的存储库中,然后您的存储库应该被注入到您的控制器中。这确保只有一个上下文实例,并且您不会遇到现在遇到的问题,即 EF 抱怨实体属于不同的上下文。

最后,我只想模仿@SteveGreene 说绝对没有理由将你的实体存储在会话中,事实上有很多理由你不应该这样做,其中最重要的是它会挫败任何努力从 In Proc 会话转移到更可靠的会话存储。一旦你使用了 StateServer、SQL Server、Redis 等,你放入会话中的任何东西必须是可序列化的,而且实体通常很难序列化,如果不是不可能的话,因为它们通常有与其他实体的许多关系,并且通常是与这些实体的循环关系。

【讨论】:

  • 只是为了让我明白:DI容器是否会保存一个上下文并仅注入它,或者它是否会为新控制器的每个实例化创建一个新上下文(每次请求都会发生这种情况)正在制作的控制器中的方法!?)。第二种行为与我目前的行为应该没有什么不同,因为随着时间的推移,仍然存在多种短暂的生活环境,没有人可以确定它们是否在任何特定时间点被处置。我也很高兴获得一些关于 IoC 容器的建议,以便在此处与 ASP.NET MVC 和 EF 一起使用。
  • 您将在“请求”范围内创建上下文。它可能会根据特定的容器被称为其他东西,但它们几乎都有某种与请求生命周期相关的范围。碰巧这将产生为每个控制器实例注入一个新上下文的效果,因为控制器是为每个请求实例化的,但这并不是您真正应该考虑的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-24
  • 1970-01-01
  • 1970-01-01
  • 2015-01-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多