是否可以将 ServiceProvider 传递给构造函数参数?
Is it possible to pass the ServiceProvider into a constructor parameter?
问题:
我们有一个 .NET 5 WPF 应用程序,它有一个 EntityFramework 核心实体 class 文件 DbEntities
,它实现了 DbContext
。我们在实例化它时使用构造函数注入。我们使用的选项之一是 AddInterceptors
,以便将访问令牌附加到 SqlConnection
。拦截器称为AzureAuthenticationInterceptor
。在注册服务时,我们希望传入 ServiceProvider
以便在拦截器构造函数中可用,可用于获取实现 Access Token 内存缓存的服务。
原因是我们有一个包含 50 多个 class 的项目,它们都使用相同的 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 class 个文件?
Program.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))
;
}
DbEntities.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));
}
首先,避免注入 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();
那么这将绕过通过构造函数注入应用依赖注入的机会。
问题:
我们有一个 .NET 5 WPF 应用程序,它有一个 EntityFramework 核心实体 class 文件 DbEntities
,它实现了 DbContext
。我们在实例化它时使用构造函数注入。我们使用的选项之一是 AddInterceptors
,以便将访问令牌附加到 SqlConnection
。拦截器称为AzureAuthenticationInterceptor
。在注册服务时,我们希望传入 ServiceProvider
以便在拦截器构造函数中可用,可用于获取实现 Access Token 内存缓存的服务。
原因是我们有一个包含 50 多个 class 的项目,它们都使用相同的 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 class 个文件?
Program.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))
;
}
DbEntities.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));
}
首先,避免注入 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();
那么这将绕过通过构造函数注入应用依赖注入的机会。