首页 > 解决方案 > 在派生的 DbContext 中获取服务

问题描述

我有以下派生的 DbContext:

public class PowerDbContext : DbContext
{
    readonly IDbAuthTokenService _authTokenService;
    readonly string _connectionString;
    readonly bool _useManagedIdentity;

    public MyDbContext(DbContextOptions<NyDbContext> options) : base(options)
    {
        _authTokenService = this.GetService<IOptionsSnapshot<AzureSqlAuthTokenService>>().Value;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var connection = new SqlConnection(_connectionString);

        connection.AccessToken = _authTokenService.GetTokenAsync().GetAwaiter().GetResult();
    }

在 Startup.cs 中:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddTransient<IDbAuthTokenService, AzureSqlAuthTokenService>();
    services.AddEntityFrameworkSqlServer();
    services.AddEntityFrameworkProxies();
    services.AddDbContextPool<MyDbContext>((serviceProvider, optionsBuilder) =>
    {
        optionsBuilder.UseLazyLoadingProxies();
        optionsBuilder.UseSqlServer(connectionString);
        optionsBuilder.UseInternalServiceProvider(serviceProvider);
    }, Configuration.GetAppSetting<int>("PoolSize"));
    ...
}

但我收到以下错误:

System.InvalidOperationException : Unable to resolve service for type 'Microsoft.Extensions.Options.IOptionsSnapshot<Myproject.Database.Services.AzureSqlAuthTokenService>'. This is often because no database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.

如何让 DbContext 解析令牌服务OnConfiguring()

更新:要清楚,我不能使用构造函数注入,因为 AddDbContextPool() 要求我派生的 DbContext 有一个只有一个参数的构造函数 (DbContextOptions)

标签: c#asp.net-core.net-coreentity-framework-core

解决方案


对于注入本身,一个简单的构造函数注入应该这样做:

public MyDbContext(DbContextOptions<NyDbContext> options, IOptionsSnapshot<AzureSqlAuthTokenService> authTokenService) : base(options)
...

但是,在我看来(至少从您提供的代码来看)您从未配置IOptionsSnapshot<AzureSqlAuthTokenService>过。相反,您配置IDbAuthTokenService指向AzureSqlAuthTokenService实现的服务。我不是 100% 确定你想走哪条路,但我看到了两种可能性:
1)注入你的IDbAuthTokenService服务:

public MyDbContext(DbContextOptions<NyDbContext> options, IDbAuthTokenService authTokenService) : base(options)
...

2)配置IOptionsSnapshot,然后注入它(见文档):
Startup.cs:

public void ConfigureServices(IServiceCollection services)
...
services.Configure<MyAzureTokenOptions>(Configuration.GetSection("Proper:Config:Section"));
...

数据库上下文:

public MyDbContext(DbContextOptions<NyDbContext> options, IOptionsSnapshot<MyAzureTokenOptions> azureTokenOptions) : base(options)
...

编辑:
我错过了您正在使用 DbContext 池的事实。是的,你是对的——在这种情况下,构造函数注入不起作用。但是其余的答案仍然适用:您注册了一个IDbAuthTokenService服务(通过它的AzureSqlAuthTokenService实现),但尝试IOptionsSnapshot<AzureSqlAuthTokenService>从服务提供者那里检索。换句话说,您正在寻找未在 DI 中注册的服务(或选项快照),这就是您收到该特定错误的原因。如上所述修复服务(或选项快照)的检索或注册。

重要提示:
如果它是您想要的服务,您应该了解一下 DbContext 池的工作原理。要点是在服务请求而不是销毁 DbContext 实例之后,它的状态被重置并返回到池中。这意味着它实际上是单例的。由于您不能将作用域(或瞬态)依赖项注入单例主体,因此您的依赖项也必须是单例的。这意味着:

public void ConfigureServices(IServiceCollection services)
...
services.AddSingleton<IDbAuthTokenService, AzureSqlAuthTokenService>();

如果你AzureSqlAuthTokenService自己依赖于其他作用域的依赖,你要么在依赖链中传播相同的原则(如果这对于所说的依赖是合理的),或者从池中切换并省去一些麻烦。


推荐阅读