关于实体框架:ASP.NET MVC中每个请求一个DbContext(没有IOC容器)

One DbContext per request in ASP.NET MVC (without IOC container)

如果已经回答了这个问题,那么很抱歉,但是如果您不使用IOC容器,如何保证每个请求都有一个实体框架dbContext?(到目前为止,我所得到的答案是关于IOC集装箱解决方案的。)

似乎大多数解决方案都与HttpContext.Current.Items字典挂钩,但如何保证在请求完成时处理dbContext?(或者,是否有必要对ef DbContext进行处置?)

编辑

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


我知道这不是最近的问题,但我还是会把我的答案贴出来,因为我相信有人会发现它很有用。

像其他许多人一样,我遵循了公认答案中提到的步骤。是的,它起作用了。但是,有一个要点:

每次发出请求时,方法BeginRequest()和EndRequest()都会激发,但不仅针对ASPX页,而且针对所有静态内容!也就是说,如果您使用上面提到的代码,并且您的页面上有30个图像,那么您将对dbContext进行30次实例化!

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
          }
      }
 }

然后处理

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

这种修改确保您在每个请求中仅实例化和处理一次上下文,并且仅在需要时才这样做。选定的答案每次都实例化上下文。

注意:db1实体是从dbContext(由vs生成)派生的。您可能希望用上下文名称来更改它;)

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

注3:同样的方法也可以在不同的情况下使用,例如当您希望共享一个sqlconnection实例或任何其他实例时…此解决方案不排斥dbContext对象,也不排斥实体框架。


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

1
2
3
4
5
6
7
8
9
10
11
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类中…

1
2
3
4
5
6
7
public class EntityContext
{
    public static EntityContext Current
    {
        get { return HttpContext.Current.Items["_EntityContext"] as EntityContext; }
    }
}


一种方法是订阅Application_BeginRequest事件,将dbContext注入当前的httpContext,并在Application_EndRequest中从httpContext获取并释放。介于两者之间的任何内容(几乎都是:-)都可以从当前的httpContext中提取dbContext并使用它。是的,你应该把它处理掉。顺便问一下,您是否有任何理由不使用已经为您做了这件事的DI框架?


查德·莫兰答案的小补充。它的灵感来自华尔特的音符。为了避免静态内容的上下文初始化,我们应该检查当前的路由处理程序(此示例仅适用于MVC):

1
2
3
4
5
6
7
8
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();
  }
}


如果您在控制器中实现IDisposable,并在Disposing方法中释放上下文,并在控制器构造函数中实例化新的上下文,那么您应该是安全的,因为控制器是为每个请求实例化的。不过,我不明白,你为什么要这样做?…您应该使用DI,或者使用上下文的一个静态实例创建一个上下文工厂。如果您不使用一个实例(每个请求一个实例),那么在某个时刻就会出现问题。未解析上下文的问题是,ef在上下文中缓存数据,如果上下文的其他实例更改了数据库中已经在另一个上下文中缓存的内容,则说明您处于不存在状态。在DI变得如此流行之前,我曾经在应用程序的某个地方有一个上下文的静态实例,这比让每个请求生成自己的上下文要快得多,而且更安全,但是您需要实现状态检查代码,以确保到DB的上下文连接是正常的…对于这个问题有很多更好的解决方案,最好是使用一些DI框架。我建议Ninject与MVC涡轮结合使用,它很容易设置,您可以通过Nuget添加它。


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