首页 > 解决方案 > DbContext 的新实例

问题描述

我有一个 .net 4 web api,其中有许多控制器都继承自同一个基本控制器。这个基本控制器有一个获取数据库连接字符串并使用它来实例化 DbContext 的新实例的方法。然后将 DbContext 传递给 10 多个存储库,这些存储库都使用该 DBContext 实例。

基本控制器正在执行以下操作:

public IRepository Repository { get; set; }

public BaseController(IRepository repository)
 {
    this.Repository = repository;
 }

protected override void Initialize(HttpControllerContext controllerContext)
 {
       base.Initialize(controllerContext);

       this.Repository = IRepositoryFactory.GetRepository();

 }

public static class IRepositoryFactory
{
    public static IRepository GetRepository()
    {
       // **gets database connectionString**
       var context = new DbContext(connectionString)

       // this passes context to all the 10+ repositories using same instance
        return new IDataRepository(context);
    }
}

让基本控制器在每个 api 调用上实例化一个新的 DbContext 实例,导致 DbContext 在调用之间永远不会被重用。在 .net 核心中,您使用启动类来告诉 DbContext 要使用哪个数据库,但我不相信您可以在 .net 4 中做到这一点。恐怕这些新的 DbContext 实例不会像 Singleton 那样使用因此没有进行缓存,导致大量不必要的数据库命中。有没有更好的方法来告诉 DbContext 它是连接字符串并使用它而不在 BaseController 中创建新的 DbContext 实例,这会在每个控制器上受到影响?

标签: asp.net-mvcentity-frameworkdbcontext

解决方案


对于 Web 应用程序,您应该利用 IoC 容器来管理依赖项和生命周期范围。其中大部分可以直接连接到 MVC 以实例化控制器,解析对依赖项(如存储库)的引用,以及它们的依赖项,包括 DbContext 或像工作单元实现这样的包装器。默认情况下,IoC 容器应该将依赖生命周期限定为请求,因此如果您有 3 个存储库为控制器调用提供服务,它们都会收到对 DbContext 的相同引用来服务请求。

如果您还没有使用过 IoC 容器/依赖注入,我建议您看看 Autofac 或 Unity。其他常见但较旧的包括 Castle Windsor & Ninject。ASP.Net Core 也有它自己的 DI 扩展。我个人推荐 Autofac,因为它很成熟,但相当年轻,所以它是围绕现代范式设计的,例如泛型和流利结构。它也有很好的文档。

DbContext 应该是短暂的,但是如果您使用多个存储库,几乎总是建议为所有存储库调用共享一个实例,以便可以在存储库之间共享实体引用,并将更改保存为一个操作的一部分。(提交或回滚一个或一个都没有。)单独的 DbContext 实例需要复杂的代码来分离和附加实体以在存储库之间共享实体,以及显式的事务处理来保护操作。我个人使用存储库模式作为隔离测试的分隔,尽管我避免使用通用存储库模式。我将 Mehdime 的 DbContextScope 模式用于工作单元,这让我可以更好地控制工作单元,而不是使用依赖注入来管理 DbContext 的生命周期。

应避免用于 Web 应用程序或任何应用程序的 DbContexts 的单例实例。首先,EF 不与数据库同步数据。任何缓存的实体很容易变得陈旧,因此上下文存在的时间越长,行版本检查(如果有的话)的陈旧覆盖或异常就会发生越多。EF 的缓存/跟踪功能还会导致内存使用量增加以及性能下降,因为 EF 希望在每个查询中检查对象引用和相关依赖项的缓存。当延迟加载被禁用时,这可能会导致一些奇怪的行为,其中 EF 将填充碰巧被缓存的引用,这些引用可能无法反映完整的数据集。


推荐阅读