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

Posted

技术标签:

【中文标题】我啥时候应该创建一个新的 DbContext()【英文标题】:When should I create a new DbContext()我什么时候应该创建一个新的 DbContext() 【发布时间】:2012-11-13 02:50:18 【问题描述】:

我目前正在使用类似于此的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();

考虑到这一点,有时一个访问者可以激活多个 DbContext,例如,如果它正在访问使用 UserRepository 的控制器,这可能不是最好的想法。

我应该什么时候创建一个新的 DbContext?或者,我是否应该有一个在所有地方传递和重用的全局上下文?这会导致性能下降吗?也欢迎提出替代方法的建议。

【问题讨论】:

在这种情况下,我将使用依赖注入(例如 Ninject),因此它会为每个请求创建一个 DbContext。我还会创建服务层。检查this SO question & answer 对于非 ASP 案例或尝试使用单例,请参阅 ***.com/questions/12871666/linq-and-datacontext(建议重复) 【参考方案1】:

我使用了一个基础控制器,它公开了一个派生控制器可以访问的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 服务器和实体框架几乎不晦涩),而且我的时间最好花在编写特定于我的应用程序的代码上。

【讨论】:

我没有什么要补充的,除了很高兴看到我自己的一些偏好反映在其他人的帖子中。我从来没有真正理解存储库示例会导致在 DbContext 上添加另一个层,而没有真正添加任何东西,而且我非常喜欢创建一个公开受保护 DbContext 的基类。 想补充一点,这个答案很好,但现在随着 EF6 的发布而过时,EF6 会在每次使用后自动处理上下文。因此,在某些情况下创建每个会话(全局)上下文是可以的。如果您使用 EF6 连接到存储过程并且数据锁定不是问题,那么在基本控制器中创建一次上下文并让所有需要数据库访问的控制器从基本控制器继承可能是理想的。最正确的答案是您需要跟上技术并为您的架构应用正确的模式。 如果我想从使用 DbContext 的控制器操作中调用一些模型方法怎么办?我怎样才能将它传递给模型方法? 然后您在控制器中执行不属于它们的数据库查询和业务逻辑。您应该创建一个从您的控制器调用的服务。 IE。你的 UserController 调用你的 UserService 中的一个方法。 @Fred,是的,我如何通过简单的参数将 DbContext 传递给 UserService?【参考方案2】:

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

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));

希望我的回答对你有帮助。

【讨论】:

如果我想在 ConfigureServices 方法本身的 Startup.cs 中使用 DBContext 怎么办?我有一个 OICD 中间件,我需要在其中访问数据库,但我无法访问 DBContext 或者我不知道如何访问? 在 configureServices 方法中,您的 DBContext 可能不可用,因为您在那里配置了它。您在运行时实际从中获取 DBContext 的 ServiceProvider 将首先在 Configure()(不是“ConfigureServices”!)方法中可用。在那里,您可以使用 ApplicationBuilder 请求上下文,方法是键入“app.ApplicationServices.GetRequiredService();” (将 MyDbContext 替换为您自己的上下文的类名)。 如果控制器注入了存储库,它们(控制器)如何保存更改?假设我正在发送一个 POST 请求以在数据库中插入一些内容,控制器处理请求,使用存储库添加新创建的对象 .. 然后呢?谁坚持更改? @MMalke 存储库可以做到这一点。它通常具有一个函数“SaveData(Data myData)”,然后存储库为 Data-Class 创建其 Entity-Framework 兄弟的实例,将其添加到 dbcontext 中的相应 dbset 中,然后调用 SaveChanges()。控制器唯一要做的就是调用 Repository.SaveData(myData) 函数。【参考方案3】:

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

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

【讨论】:

【参考方案4】:

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

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);
    

【讨论】:

虽然您的方法没有任何问题,但我相信实体框架上下文将以惰性方式完成所有操作,并且在您实际访问数据库之前不会执行任何实际工作。所以创建 EF 上下文的开销应该很小。 我做了一些研究,似乎是正确的。我做了一些简单的测试,比较了GC.GetTotalMemory() 返回的内容(不完美,但这是我发现的),差异从未超过 8Kb。【参考方案5】:

您应该在每次 Save() 操作后立即释放上下文。否则每次后续保存都需要更长的时间。我有一个项目在一个循环中创建和保存复杂的数据库实体。令我惊讶的是,在循环中移动“using (var ctx = new MyContext())...”后,操作速度提高了三倍。

【讨论】:

【参考方案6】:

这显然是一个较老的问题,但是如果您使用 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);
        
    

【讨论】:

以上是关于我啥时候应该创建一个新的 DbContext()的主要内容,如果未能解决你的问题,请参考以下文章

我啥时候应该在 C 中使用 malloc,啥时候不应该?

我啥时候应该在同一个视图控制器中使用多个视图?

我啥时候应该销毁令牌[关闭]

我啥时候应该在 python 运算符上使用特定的气流运算符?

我啥时候应该在 C++ 中使用 typedef?

我啥时候应该增加 xcdatamodeld 版本?