是否可以将 ServiceProvider 传递给构造函数参数?

J W*_*ezy 1 c# dependency-injection azure-sql-database azure-web-app-service .net-5

问题:

我们有一个 .NET 5 WPF 应用程序,它有一个 EntityFramework Core 实体类文件DbEntities,它实现了DbContext. 我们在实例化它时使用构造函数注入。我们使用的选项之一是AddInterceptors将访问令牌附加到SqlConnection. 拦截器被称为AzureAuthenticationInterceptor。在注册服务时,我们希望传入ServiceProvider以便它在拦截器构造函数中可用,可用于获取实现访问令牌内存缓存的服务。

原因是我们有一个包含 50 多个类的项目,这些类都使用同一个DbEntities文件,在构造函数中使用 0 个参数。这已升级到 .NET 5,其中避免了依赖注入,因为需要将其应用于所有表单。因此, 以DbEntities具有 的形式实例化new DbEntities();

但是,在这种情况下,我们正在实现访问令牌缓存,需要将其注册为服务。否则,如果我们每次创建 new 时都实例化缓存DbContext,那么缓存将被清除。

使用此方法实现访问令牌内存缓存https://mderriey.com/2020/09/12/resolve-ef-core-interceptors-with-dependency-injection/

我们只想对内存中的令牌缓存使用依赖注入。我们想到的唯一捷径就是ServiceProvider在拦截器的构造函数中传入,但是在ConfigureServices方法中并没有出现。

题:

可以通过ServiceProvider吗?如果没有,有没有其他方法可以在拦截器上实现依赖注入而无需更改50个类文件?

程序.cs

Public static void Main()
{
...
    Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
        .ConfigureAppConfiguration((context, builder) =>
        {
            builder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
        })
        .ConfigureServices((context, services) =>
        {
            Configuration = context.Configuration;
            ConfigureServices(Configuration, services);
        })
        .Build();
...
}

private static void ConfigureServices(IConfiguration objConfiguration, IServiceCollection objServices)
{
    objServices.AddMemoryCache()
        .AddSingleton<IAzureSqlTokenProvider, AzureIdentityAzureSqlTokenProvider>()
        .Decorate<IAzureSqlTokenProvider, CacheAzureSqlTokenProvider>()
        .AddSingleton(new AzureAuthenticationInterceptor(IServiceProvider_NeededHere))
        ;
}
Run Code Online (Sandbox Code Playgroud)

数据库实体.cs

public DbEntities() :
base(new DbContextOptionsBuilder<DbEntities>()
    .UseSqlServer(ConfigurationManager.ConnectionStrings["DbEntities"].ConnectionString)
    .AddInterceptors(new AzureAuthenticationInterceptor())
    .Options)
{ }
Run Code Online (Sandbox Code Playgroud)

AzureAuthenticationInterceptor.cs

public AzureAuthenticationInterceptor(IServiceProvider objServiceProvider)
{
    this.IAzureSqlTokenProvider = (IAzureSqlTokenProvider)objServiceProvider.GetService(typeof(IAzureSqlTokenProvider));
}   
Run Code Online (Sandbox Code Playgroud)

Nko*_*osi 5

首先,避免注入IServiceProvider,这是一种代码异味,会导致设计不佳。

重构 AzureAuthenticationInterceptor.cs

public AzureAuthenticationInterceptor(IAzureSqlTokenProvider tokenProvider) {
    this.IAzureSqlTokenProvider = tokenProvider;
}   
Run Code Online (Sandbox Code Playgroud)

这样就可以根据需要注入显式依赖项

//...

.AddSingleton<AzureAuthenticationInterceptor>()

//...
Run Code Online (Sandbox Code Playgroud)

在配置 DbEntities 时解析拦截器时

//...

services.AddDbContext<DbEntities>((provider, options) => {
    options.UseSqlServer(Configuration.GetConnectionString("<connection-string-name>"));
    options.AddInterceptors(provider.GetRequiredService<AzureAuthenticationInterceptor>());
});

//...
Run Code Online (Sandbox Code Playgroud)

请注意,如果您使用默认构造函数手动初始化上下文,即:new DbEntities();那么这绕过了通过构造函数注入应用依赖注入的机会。