我什么时候应该创建一个新的DbContext()

时间:2012-11-25 12:40:22

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

我目前正在使用与此类似的DbContext

namespace Models
{
    public class ContextDB: DbContext
    {

        public DbSet<User> Users { get; set; }
        public DbSet<UserRole> UserRoles { get; set; }

        public ContextDB()
        {

        }
    }
}

然后我在 ALL 我需要访问数据库的控制器的顶部使用以下行。我也在我的UserRepository类中使用它,它包含与用户相关的所有方法(例如获取活动用户,检查他拥有的角色等):

ContextDB _db = new ContextDB();

考虑到这一点......当一个访问者可以有多个DbContexts活动时,就会出现这种情况。如果他访问使用UserRepository的控制器..这可能不是最好的想法,我有几个问题

  1. 我应该何时制作新的DbContext /我是否应该传递一个全局上下文?
  2. 我可以在所有地方重复使用一个全局上下文吗?
  3. 这会导致性能下降吗?
  4. 其他人如何做到这一点?

6 个答案:

答案 0 :(得分:73)

我使用一个基本控制器来公开派生控制器可以访问的DataBase属性。

public abstract class BaseController : Controller
{
    public BaseController()
    {
        Database = new DatabaseContext();
    }

    protected DatabaseContext Database { get; set; }

    protected override void Dispose(bool disposing)
    {
        Database.Dispose();
        base.Dispose(disposing);
    }
}

我的应用程序中的所有控制器都来自BaseController,并且使用如下:

public class UserController : BaseController
{
    [HttpGet]
    public ActionResult Index()
    {
        return View(Database.Users.OrderBy(p => p.Name).ToList());
    }
}

现在回答你的问题:

  

我应该何时制作新的DbContext /我应该有一个全局上下文   我经过?

应根据请求创建上下文。创建上下文,做你需要做的事情然后摆脱它。使用我使用的基类解决方案,您只需担心使用上下文。

不要尝试拥有全局上下文(这不是Web应用程序的工作方式)。

  

我是否可以在所有地方重复使用一个全局上下文?

不,如果你保留一个上下文,它将跟踪所有更新,添加,删除等,这将减慢你的应用程序,甚至可能会导致一些非常微妙的错误出现在你的应用程序中。

您应该选择将您的存储库公开给您的控制器,但不要两者都公开。如果两个上下文对应用程序的当前状态有不同的看法,那么从同一个方法访问两个上下文会导致错误。

就个人而言,我更喜欢直接公开DbContext,因为我看到的大多数存储库示例最终只是DbContext周围的薄包装。

  

这会导致性能下降吗?

第一次创建DbContext非常昂贵,但是一旦完成此操作,就会缓存大量信息,以便后续实例化更快。每次需要访问数据库时,您更容易看到保持上下文的性能问题而不是实例化。

  

其他人如何做到这一点?

取决于。

有些人更喜欢使用依赖注入框架在创建控制器时将其上下文的具体实例传递给控制器​​。两种选择都很好。我更适合小规模的应用程序,你知道使用的特定数据库不会改变。

有些人可能会说你不能知道这一点,这就是依赖注入方法更好的原因,因为它使你的应用程序更容易适应变化。我对此的看法是它可能不会改变(SQL服务器和实体框架几乎不会模糊),并且我最好花时间编写特定于我的应用程序的代码。

答案 1 :(得分:5)

我尝试根据自己的经验进行回答。

1。我什么时候应该创建一个新的DbContext /我应该传递一个全局上下文?

Context应该通过依赖项注入来注入,而不应该由您自己实例化。最佳实践是通过依赖项注入将其创建为范围服务。 (请参阅我对问题4的回答)

也请考虑使用适当的分层应用程序结构,例如Controller> BusinessLogic> Repository。在这种情况下,控制器不是接收db-context,而是接收存储库。在控制器中注入/实例化db-context告诉我,您的应用程序体系结构在一个地方混合了许多职责,在任何情况下,我都不建议这样做。

2。我可以在所有地方重复使用一个全局上下文吗?

是的,您可以拥有,但问题应该是“ 应该我有...”->否。应该根据每个请求使用上下文来更改您的存储库,然后再次将其删除。

3。这会导致性能下降吗?

是的,因为DBContext根本不是为全局而设计的。它存储已输入或查询到的所有数据,直到销毁为止。这意味着全局上下文将变得越来越大,对其进行的操作将越来越慢,直到您遇到内存不足的异常或您死了,因为它们都慢下来了。

当多个线程一次访问同一上下文时,您还将获得异常和许多错误。

4。其他人怎么做?

DBContext通过工厂的依赖关系注入而注入;范围:

services.AddDbContext<UserDbContext>(o => o.UseSqlServer(this.settings.DatabaseOptions.UserDBConnectionString));

希望我的回答对您有所帮助。

答案 2 :(得分:0)

现在我正在尝试这种方法,这可以避免在调用不使用它的操作时实例化上下文。

public abstract class BaseController : Controller
{
    public BaseController() { }

    private DatabaseContext _database;
    protected DatabaseContext Database
    {
        get
        {
            if (_database == null)
                _database = new DatabaseContext();
            return _database;
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (_database != null)
            _database.Dispose();
        base.Dispose(disposing);
    }
}

答案 3 :(得分:0)

这显然是一个较旧的问题,但如果您使用DI,您可以执行类似的操作,并在请求的生命周期内调整所有对象的范围

 public class UnitOfWorkAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.BeginTransaction();
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.CloseTransaction(actionContext.Exception);
        }
    }

答案 4 :(得分:0)

您应该在每次Save()操作之后立即处理上下文。否则,随后的每次保存将花费更长的时间。我有一个项目,可以周期创建和保存复杂的数据库实体。令我惊讶的是,在周期内移动“使用(var ctx = new MyContext()){...}”之后,该操作的速度提高了三倍。

答案 5 :(得分:0)

从性能的角度来看,DbContext 应该在实际需要时创建 just,例如,当您需要在业务层内拥有用户列表时,您可以从 DbContext 中创建一个实例并immediately dispose当您的工作完成后

using(var context=new DbContex())
{
var users=context.Users.Where(x=>x.ClassId==4).ToList();

}

context 实例将在离开 Using 块后被释放。

但如果不立即处理会怎样?
DbContext本质上是一个缓存,查询次数越多,占用的内存块就越多。
concurrent requests 涌向您的应用程序的情况下,它会更加明显,在这种情况下,您占用内存块的每一毫秒都至关重要,更不用说一秒了。
你越推迟处理不必要的对象,你的应用程序就越容易崩溃!

当然,在某些情况下,您需要保留您的 DbContext 实例并在代码的另一部分但在同一个 Request Context 中使用它。

我建议您访问以下链接以获取有关管理 DbContext 的更多信息:
dbcontext Scope