首页 > 解决方案 > 是否可以将 ServiceProvider 传递给构造函数参数?

问题描述

问题:

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

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

但是,在这种情况下,我们正在实现一个访问令牌缓存,它需要注册为服务。否则,如果我们只是在每次创建新的时实例化缓存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))
        ;
}

数据库实体.cs

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

AzureAuthenticationInterceptor.cs

public AzureAuthenticationInterceptor(IServiceProvider objServiceProvider)
{
    this.IAzureSqlTokenProvider = (IAzureSqlTokenProvider)objServiceProvider.GetService(typeof(IAzureSqlTokenProvider));
}   

标签: c#dependency-injectionazure-web-app-serviceazure-sql-database.net-5

解决方案


首先,避免注入IServiceProvider,这是一种代码味道,会导致糟糕的设计。

重构 AzureAuthenticationInterceptor.cs

public AzureAuthenticationInterceptor(IAzureSqlTokenProvider tokenProvider) {
    this.IAzureSqlTokenProvider = tokenProvider;
}   

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

//...

.AddSingleton<AzureAuthenticationInterceptor>()

//...

在配置 DbEntities 时解析拦截器时

//...

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

//...

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


推荐阅读