DbContext用于多个控制器 - 怎么做?

时间:2015-12-23 17:26:23

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

我的上下文用于两个控制器(AdminController,它们可以查看和添加一些数据,HomeController只能查看数据。

我可以让我的上下文类只是我的控制器类中的字段吗?或者我应该把它变成单身?或者我应该创建一个额外的单例类,它在我的网站中存储所有DbContext类?该如何正确完成?

3 个答案:

答案 0 :(得分:3)

每个HTTP请求的上下文

我相信ASP.NET MVC的最佳方式是每个请求使用1个DbContext。您将受益于此方法,因为:

  1. 您不需要关心上下文中的对象,因为上下文是唯一的,因此如果它们来自不同的上下文,则无需附加实体。
  2. 您不必手动处理上下文。虽然必须处理上下文,但我们会在处理请求时在一个地方执行。
  3. 您可以在一个位置创建上下文。这是单元测试的一个很好的抽象。
  4. 当然你可以使用IoC容器,但我不喜欢开销,因为你可以自己轻松编写所有必要的逻辑。

    所以我们需要DbContext的包装器。它会将您的EF上下文保留在HttpContext项中。在ASP.NET MVC应用程序中,您可以将初始化放在基本控制器中:

    public class DbContextHelper : IDbContextHelper // interface for testing if you need it
    {
        private const string contextKey = "MyContext";
    
        public MyContext GetContext()
        {
            if (HttpContext.Current.Items[contextKey] == null)
            {
                HttpContext.Current.Items.Add(contextKey, new MyContext());
            }
    
            return (MyContext) HttpContext.Current.Items[contextKey];
        }
    
        public void DisposeContext()
        {
            if (HttpContext.Current.Items[contextKey] != null)
            {
                var context = (MyContext) HttpContext.Current.Items[contextKey];
                context.Dispose();
            }
        }
    }
    

    现在要完成设置,我们需要将DisposeContext调用注入End_Request事件。这取决于您使用的ASP.NET版本。通常我会创建一个处理上下文的ActionFilter。并在全球注册。

    public class DisposeDbContextFilterAttribute : ActionFilterAttribute
    {
        private static readonly DbContextHelper contextHelper = new DbContextHelper();
    
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            contextHelper.DisposeContext();
        }
    }
    

    现在,当所有设置完成后,只需享受使用它:

    var user = contextHelper.GetContext().Users.Find(userId);
    

答案 1 :(得分:1)

这是一个很好的架构问题,这是一个很好的资源,你可以查看:

http://www.davepaquette.com/archive/2013/03/27/managing-entity-framework-dbcontext-lifetime-in-asp-net-mvc.aspx

就个人而言,我倾向于允许您的IoC容器管理为您创建连接并将它们注入您的控制器。或者,如果您使用存储库或类似的体系结构,则可以将上下文直接注入到这些类中,并将存储库注入控制器。

如果对于您的应用程序而言过于复杂,则使用示例链接中的任何其他方法都不会出现严重的性能问题。实体将利用连接池,因此没有那么多的开销。

我不希望我的上下文成为单例,因为我希望每个Web请求都包含自己的上下文对象。这有很多原因,如果你继续跟踪,其中最重要的是性能。单例上下文也可能为奇怪的事务问题敞开大门 - 请记住,Web是一个多线程环境。

请记住,没有正确或错误的答案,因为您提出的是架构问题而不是一个架构适用于每个应用程序。如果您的解决方案易于理解,易于维护并采用最佳实践,那么您就可以做得很好。

希望这些信息有所帮助,祝你好运!

答案 2 :(得分:0)

  

我可以让我的上下文类只是我的控制器类中的字段吗?

不确定。您可以在控制器中创建一个私有字段来保留db上下文类的实例。

你不需要单身。来到您的AdminController和HomeController的请求是2个不同的HTTP请求,它应该创建&为每个http请求使用DbContext类的新对象..

public class AdminController
{
  private YourDbContext db;
  public AdminController()
  {
    db = new YourDbContext();
  } 
  public ActionResult Users()
  {
    var list=db.Users.ToList();
    //use list as needed now
  }
}

使用这种方法,只有问题是,即使您请求的动作方法没有使用任何数据,也会创建YourDbContext的对象。

如果需要,您可以创建一个YourBaseController并在那里创建DbContext对象初始化,并且两个控制器都可以从中继承。

public class MyBaseController : Controller
{
   protected YourDbContext db;
   public MyBaseController ()
   {
      db = new YourDbContext();
   }   
}
public class AdminController : MyBaseController
{

}

由于你没有从内存中处理DbContext对象,我们将依赖点网垃圾收集器。

但是如果你愿意,你可以创建一个DbContext类的对象,使用它并在使用后通过使用using关键字

来处置它。
public ActionResult View()
{
  var name= new List<string>();
  using(var db=new YourDbContext())
  {
     name=db.Users.Select(s=>s.Name);
  }
  //to do : return something
}

使用表达式,框架将在它退出using { }

的范围后处置YourDbContext实例

此外,查看dependency injection可能是一个好主意,这样您就不会手动使用new关键字创建对象,但框架会为您注入这些对象。这将有助于您使代码更适合单元测试。