【问题标题】:DbContext being used in multiple controllers- how to do it?DbContext 在多个控制器中使用 - 怎么做?
【发布时间】:2016-03-30 04:36:09
【问题描述】:

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

我可以让我的上下文类只是我的控制器类中的字段吗?或者我应该让它成为一个单身人士?或者我应该创建一个额外的单例类,将所有DbContext 类存储在我的网站中?应该怎么做呢?

【问题讨论】:

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


    【解决方案1】:

    这是一个很好的架构问题,您可以查看以下优秀资源:

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

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

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

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

    请记住,当您问的是架构问题时,没有正确或错误的答案,而且没有一种架构适用于每个应用程序。如果您的解决方案易于理解、易于维护并且采用了最佳实践,那么您的工作就做得很好。

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

    【讨论】:

      【解决方案2】:

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

      当然。您可以在控制器中创建一个私有字段来保留您的数据库上下文类的实例。

      您不需要将其设为单例。到达 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 对象,因此我们将依赖 dot net 垃圾收集器。

      但是,如果您愿意,您可以创建 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 实例退出 using { } 的范围后处理它

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

      【讨论】:

        【解决方案3】:

        每个 HTTP 请求的上下文

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

        1. 您不需要关心上下文中的对象,因为上下文是唯一的,因此如果实体来自不同的上下文,则无需附加。
        2. 您不必手动处置上下文。虽然必须处理上下文,但我们会在处理请求时在一个地方处理。
        3. 您在一处创建上下文。这是一个很好的单元测试抽象。

        当然你可以使用 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);
        

        【讨论】:

        猜你喜欢
        • 2021-07-01
        • 1970-01-01
        • 2011-12-04
        • 1970-01-01
        • 2017-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多