ASP.NET MVC中每个请求一个DbContext(没有IOC容器)

时间:2011-06-13 18:29:37

标签: asp.net-mvc entity-framework asp.net-mvc-3 entity-framework-4.1 ef-code-first

如果已经回答这个问题,请道歉,但如果您不使用IOC容器,如何保证每个请求有一个Entity Framework DbContext? (到目前为止,我遇到的答案涉及IOC容器解决方案。)

似乎大多数解决方案都挂钩到HttpContext.Current.Items字典,但是如何在请求完成时保证处理DbContext? (或者EF DbContext并非绝对必要的处理?)

修改

我目前正在我的控制器中实例化和处理我的DbContext,但我在ActionFilters和我的MembershipProvider中也有几个单独的DbContext实例(我刚注意到,也有几个验证器)。因此,我认为集中我的DbContext的实例化和存储以减少开销可能是一个好主意。

6 个答案:

答案 0 :(得分:72)

我知道这不是最近的问题,但无论如何我都会发布我的答案,因为我相信有人会发现它很有用。

与其他许多人一样,我按照接受的答案中提到的步骤进行了操作。是的,它有效。 HOWEVER ,有一个问题:

方法BeginRequest()和EndRequest()每次发出请求时都会触发,但不仅适用于aspx页面,还适用于所有静态内容!也就是说,如果您使用上面提到的代码并且您在页面上有30个图像,那么您将重新实例化dbcontext 30次!

解决方法是使用包装类来检索上下文,如下所示:

internal static class ContextPerRequest
{
      internal static DB1Entities Current
      {
          get
          {
              if (!HttpContext.Current.Items.Contains("myContext"))
              {
                  HttpContext.Current.Items.Add("myContext", new DB1Entities());
              }
              return HttpContext.Current.Items["myContext"] as DB1Entities;
          }
      }
 }

然后处理

protected void Application_EndRequest(object sender, EventArgs e)
{
   var entityContext = HttpContext.Current.Items["myContext"] as DB1Entities;
   if (entityContext != null) 
      entityContext.Dispose();
}

此修改可确保您仅在每个请求时仅在需要时实例化和处置您的上下文一次。选定的答案每次都会实例化上下文。

注意: DB1Entities派生自DbContext(由VS生成)。你可能想用你的上下文名称改变它;)

注意2:在这个例子中,我只使用一个dbcontext。如果您需要使用多个,则需要根据需要修改此代码。不要把它作为世界问题的最终解决方案,因为它肯定不是最终产品。它只是为了提示,如何以一种非常简单的方式实现它。

注3:同样的方法也可以在不同的情况下使用,例如,当你想共享一个SqlConnection实例或任何其他...时,这个解决方案不是唯一的DbContext对象,也不是实体框架。

答案 1 :(得分:60)

我会使用BeginRequest / EndRequest方法,这有助于确保在请求结束时正确处理您的上下文。

protected virtual void Application_BeginRequest()
{
    HttpContext.Current.Items["_EntityContext"] = new EntityContext();
}

protected virtual void Application_EndRequest()
{
    var entityContext = HttpContext.Current.Items["_EntityContext"] as EntityContext;
    if (entityContext != null)
        entityContext.Dispose();
}

在你的EntityContext类中......

public class EntityContext
{
    public static EntityContext Current
    {
        get { return HttpContext.Current.Items["_EntityContext"] as EntityContext; }
    }
}

答案 2 :(得分:10)

一种方法是订阅Application_BeginRequest事件,将DbContext注入当前的HttpContext和HttpContext的Application_EndRequest提取并处理。介于两者之间的任何东西(几乎所有东西:-))都可以从当前的HttpContext中获取DbContext并使用它。而且,是的,你应该处理它。顺便说一句,你有没有理由不使用已经为你做过的DI框架以及其他有用的东西?

答案 3 :(得分:7)

Chad Moran的小补充回答。它受到沃尔特笔记的启发。为了避免静态内容的上下文初始化,我们应该检查当前的路由处理程序(这个例子仅适用于MVC):

protected virtual void Application_BeginRequest()
{
  var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(this.Context));
  if (routeData != null && routeData.RouteHandler is MvcRouteHandler)
  {
    HttpContext.Current.Items["_EntityContext"] = new EntityContext();
  }
}

答案 4 :(得分:1)

如果在控制器中实现IDisposable,并在disposing方法中配置上下文,并在控制器构造函数中实例化新上下文,那么在为每个请求实例化控制器时应该是安全的。 但是,我没有看到你为什么要那样做? ... 您应该使用DI,或者使用一个静态上下文实例创建上下文工厂。如果你不使用一个实例(你为每个请求创建一个实例),那么在某些时候你会遇到问题。不受干扰的上下文的问题是EF在上下文中缓存数据,并且如果某个其他上下文实例在DB中已经在另一个上下文中缓存了某些内容 - 则您具有不一致的状态。在DI变得如此受欢迎之前,我曾经在应用程序中的某个地方有一个静态的上下文实例,这比让每个请求创建自己的上下文要快得多,也更安全,但是你需要实现状态检查代码来确保上下文与数据库的连接是正确的...这个问题有很多更好的解决方案,最好是使用一些DI框架。我建议将Ninject与MVCTurbine结合使用,它很容易设置,你可以通过NuGet添加它。

答案 5 :(得分:0)

这里的滑坡是不一致的状态。如果您的应用程序将拥有多个用户,并且他们有可能同时更改数据,那么如果您保留单个上下文,则可能会遇到数据完整性问题。