【问题标题】:Is there a way to factory inject a custom DbContext?有没有办法工厂注入自定义 DbContext?
【发布时间】:2022-01-21 02:45:16
【问题描述】:

...也许在 EF Core 扩展中的 AddDbContextFactory<TContext, TFactory> 中使用 TFactory?

我只看到 AddDbContextFactory examples 仅与 TContext 泛型一起使用。他们总是非常明确地说你have to use a using statement

在类似的情况下(当我在 Angular 中使用Class 或在 .NET Core 中使用 AddScoped 时),我将变量 我想在构造函数中查看设为第一个泛型参数,第二个泛型参数实际得到什么注入。你知道的,比如:

services.AddScoped<IService, RealService>();

显然,情况并非如此

services.AddDbContextFactory<ADbContextIHaveBeenInjecting, AFactoryThatWillReturnADbContextIHaveBeenInjecting>();

我希望这将消除整个使用的需要。

有没有另一种方法可以做到这一点,而不必重新编写每个注入的 DbContext 以符合他们的规定:

public void DoSomething()
{
    using (var context = _contextFactory.CreateDbContext())
    {
        // ...
    }
}

正如我所说,我希望在工厂中使用这样的东西:

public class MyDbContextFactory : IDbContextFactory<MyDbContext>
{

    public MyDbContextFactory(DbContextOptions options)
    {
    }

    public MyDbContext CreateDbContext()
    {
        var ProviderName = GetProviderName();
   
        switch (ProviderName)
        {
            case "System.Data.SqlClient":
                return new SqlServerDbContext(new DbContextOptionsBuilder<SqlServerDbContext>().UseSqlServer(ConnectionString).Options);
            case "Npgsql":
                return new PostgreSqlDbContext(new DbContextOptionsBuilder<PostgreSqlDbContext>().UseNpgsql(ConnectionString).Options);
            default:
                throw new NullReferenceException("Missing provider name for DbContext. Should be Npgsql or System.Data.SqlClient");
        }
    }

 
}

然后,在 Startup.cs ConfigureServices 中进行设置,如:

services.AddDbContextFactory<MyDbContext, MyDbContextFactory>();

所以我可以像这样注入一个类:

public class MyController : BaseApiController
{

    private readonly MyDbContext _myDbContext;


    public MyController(MyDbContext myDbContext)
    {
        _myDbContext = myDbContext;
    }


    [HttpGet("GetACount")]
    public IActionResult GetACount()
    {
        var count = _myDbContext.MyRecord.Count();

        return Ok(count);
    }
...

有没有办法使用 AddDbContextFactory 做到这一点? TFactory 实际上是做什么用的?有没有其他方法可以做到这一点?

【问题讨论】:

  • 你希望GetProviderName如何实现?
  • 这只是一个“不让人分心的例子”。小故事:注入的配置会实时更新,我们从中获取 ProviderName。
  • 您不需要使用上下文工厂。只需使用 IOptions 并映射到 json 文件中的一个部分,如下所示: "DatabaseConnections": { "OracleConnections": [ { "Alias": "Optional", "ConnectionString": "Required" }, { "Alias": "Optional ", "ConnectionString": "Required" } ], "MSSqlConnections": [ { "Alias": "Optional", "ConnectionString": "Required" } ], "SqliteConnections" [ { "Alias": "Optional", " ConnectionString": "必需" } ] }
  • 所有提供者都继承自 IDbConnection。你可以使用它作为你的接口注入到你的容器中。

标签: c# entity-framework-core dbcontext


【解决方案1】:

DbContextFactory 专门旨在要求您管理 DbContext 的生命周期,因为 Blazor 服务器应用程序不像 ASP.NET Core 那样使用 Scope-per-Http 请求,因此 Scoped DbContext 不起作用。

如果您想要 Scoped DbContext,只需使用 .AddDbContext 而不是 .AddDbContextFactory

如果你已经注册了一个 DbContextFactory 但仍想在你的服务中直接注入一个作用域 DbContext,那么像这样注册它:

    services.AddScoped<MyDbContext>(sp =>
    {
        var cf = sp.GetRequiredService<IDbContextFactory<MyDbContext>>();

        var db = cf.CreateDbContext();

        return db;

    });

【讨论】:

  • 谢谢,大卫。当时的想法是添加 DbContextFactory 对于处理并发 dbcontexts 至关重要。仅使用 AddDbContext 就可以解决这个问题吗? ...通过您提供的示例,这是否会在代码运行时创造在 dbContexts 之间切换的机会? ...因此,每次使用 dbContext 时,它都会检查 ProviderName 并使用 switch/if-else 根据该 ProviderName 返回上下文?
  • 在单个控制器中使用并发 DbContext 实例需要 DbContextFactory,并且每个 HTTP 请求都选择退出单个 DbContext。是的,这个工厂方法在对 DbContext 的每个请求上运行,因此您可以在那里添加条件逻辑。但是你可以通过这个重载services.AddDbContext&lt;MyDbContext&gt;((sp, options) =&gt;做到这一点
  • 我明白了。是否还有其他需要做的事情,“每个 HTTP 请求选择退出单个 DbContext”? (除了使用 DbContextFactory 之外?)......似乎没有办法只动态注入不同的 DbContext 子类,所以我必须使用 using 语句。对吗?
  • 可能有更好的方法。但是这个问题太混乱了,无法回答。发布一个新问题。您是否尝试在 RDBMS 之间切换每个请求?您是否需要不同的 DbContext 子类型,还是建议的解决方案的一部分?您使用的是什么框架,即 ASP.NET Core MVC?还有什么?
  • 对于任何阅读的人,我们决定和我的工厂一起建造这个建筑:services.AddDbContextFactory&lt;MyDbContext, MyDbContextFactory&gt;(); services.AddScoped&lt;MyDbContext&gt;(p =&gt; p.GetRequiredService&lt;IDbContextFactory&lt;MyDbContext&gt;&gt;().CreateDbContext()); 现在可以工作了。谢谢,大卫。
【解决方案2】:

依赖注入具体类。创建一个工厂以按类型选择子类。创建具有私有 DbContext _dbContext 的父类。从父类继承子类,用子类的:base(dbContext)调用父类构造函数。父类现在可以在其方法中访问子类上下文。子类可以共享父类的方法(添加、选择、更新和删除,通过设置子类的数据上下文)。子类将在其构造函数中依赖注入特定的 dbcontext,并在其构造函数中设置父类 dbcontext 变量。然后子类存储库类可以访问其主体中的基类方法。

在启动时定义子类存储库模式

在公共 void ConfigureServices(IServiceCollection 服务)

  var connectionString = Configuration.GetConnectionString("ABCContext_DbCoreConnectionString");
  services.AddDbContext<ABCContext>(options1 => options1.UseSqlServer(connectionString));

 services.AddTransient<IRepositoryMySubclass, RepositoryMySubclass>();

子类

  public RepositorySubclass(ABCContext dbContext) : base(dbContext)
    {
        _dbContext = dbContext;
    }

【讨论】:

  • 谢谢。运行 Startup.cs 后,我如何在 SqlServerDbContext 和 PostgreSQLDbContext 之间动态切换?
  • 您可以在构造函数中依赖注入两个或其中一个子类 dbcontext。然后依赖注入解析器将创建具体类。否则你必须使用泛型并调用工厂来构建你的具体类——类似于服务。
  • 依赖注入发生在repository子类的子类的构造函数中。如有必要,您可以根据泛型类型调用子类。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-28
相关资源
最近更新 更多