根提供者注入到由新作用域提供者解析的服务中
我有一个Blazor服务器应用程序。我有一个 IHostedService,它打算在 StartAsync() 方法中执行启动任务:
builder.Services.AddHostedService<StartupTask>();
它的构造函数注入了 IServiceScopeFactory。它创建了一个新作用域:
var provider = serviceScopeFactory
.CreateScope()
.ServiceProvider;
provider
.GetRequiredService<EntityService>()
.ReturnMyCtorInjectedProvider()
.GetRequiredService<EntityService>();
其中 EntityService 是一个 DbContext,它接收一个在构造函数中注入的 IServiceProvider。
据我所知,由于 EntityService 已经衍生自某个作用域的 IServiceProvider,它本应接收一个同样绑定在该作用域上的 IServiceProvider,从中解析同一服务的行为应该与从新创建的作用域的 IServiceProvider 解析该服务时的行为相同,但我却遇到了一个众所周知的错误:
作用域服务无法从根提供程序解析
所以看起来构造函数似乎被注入了根提供程序,但这背后的原因是什么?我到底漏掉了什么?
完整代码 - 这是 EntityService 的构造函数:
public class EntityService (DbContextOptions<EntityService> options,
IServiceProvider serviceProvider,
IEncryptionService encryptionService) : DbContext(options)
{
// ...
}
这是我的 IHostedService 的构造函数:
public class StartupCleaner(IServiceScopeFactory serviceScopeFactory) : IHostedService
{
// ...
}
它们的注册如下:
builder.Services.AddDbContextPool<EntityService>(
opts => opts.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))
);
builder.Services.AddHostedService<StartupCleaner>();
这是在 StartupCleaner 启动时调用的方法:
public Task StartAsync(CancellationToken cancellationToken)
{
var sp = serviceScopeFactory
.CreateScope()
.ServiceProvider;
var entityService = sp.GetRequiredService<EntityService>();
entityService.ReturnMyCtorInjectedProvider()
.GetRequiredService<EntityService>();
// ...
}
这是 EntityService 中的方法:
public IServiceProvider ReturnMyCtorInjectedProvider()
{
return serviceProvider;
}
我得到的错误:
无法从根提供程序解析作用域服务 'Axoneckend.Services.EntityService'
解决方案
这并不是作用域以及依赖注入总体工作原理的问题,而是你选择使用 AddDbContextPool 的问题:
备注
DbContext池化可以在高吞吐量场景中通过重用上下文实例来提升性能。然而,对于大多数应用而言,这种性能提升很小。请注意,在使用池化时,上下文的配置在使用之间不能改变,并且 注入到上下文中的作用域服务将只会从初始作用域解析一次。只有在性能测试表明它确实带来提升时,才考虑使用DbContext池化。
所以,我认为这里的初始作用域就是根作用域,它被用来创建那些池化实例。你手动创建的作用域并不是传递给构造函数的那个作用域。
下面给出一个最小示例,确认普通(非池化)作用域解析按你预期地工作:
public static void Main() {
var serviceCollection = new ServiceCollection();
serviceCollection.AddScoped<DbContext>();
serviceCollection.AddSingleton<HostedServiceSingleton>();
var serviceProvider = serviceCollection.BuildServiceProvider();
_ = serviceProvider.GetService<HostedServiceSingleton>();
}
public class DbContext {
public IServiceProvider ReturnMyCtorInjectedProvider { get; }
public DbContext(IServiceProvider provider) {
Console.WriteLine(provider.GetHashCode());
ReturnMyCtorInjectedProvider = provider;
}
}
public class HostedServiceSingleton {
public HostedServiceSingleton(IServiceScopeFactory scopeFactory) {
var provider = scopeFactory.CreateScope().ServiceProvider;
Console.WriteLine(provider.GetHashCode());
// no surprises
var injectedProvider = provider.GetService<DbContext>()
.ReturnMyCtorInjectedProvider;
// same hash code
Console.WriteLine(injectedProvider.GetHashCode());
var anotherDbContextResolution = injectedProvider
.GetService<DbContext>();
}
}
示例输出:
30631159
30631159
30631159