In my API I register some services with AddHostedService
that need some interfaces to be injected on them, for example:
Program.cs:
builder.Services.AddScoped<IHypervisorRepository, HypervisorRepository>();
builder.Services.AddScoped<IAccountRepository, AccountRepository>();
builder.Services.AddScoped<ICloudStackDbUnitOfWork, CloudStackDbUnitOfWork>();
builder.Services.AddScoped<ICloudStackCoreConnectionFactory, MySqlCloudStackCoreConnectionFactory>();
builder.Services.AddScoped<IJobUtility, JobUtility>();
builder.Services.AddScoped<IVdcUnitOfWork, VdcUnitOfWork>();
builder.Services.AddScoped<IMemoryCache, MemoryCache>();
builder.Services.AddHostedService<CheckDatabaseHealthBackgroundService>();
CheckDatabaseHealthBackgroundService.cs:
public sealed class CheckDatabaseHealthBackgroundService : BackgroundService, IDisposable
{
private readonly IServiceProvider _provider;
private readonly IJobUtility _jobUtility;
private readonly IVdcUnitOfWork _unitOfWork;
private readonly ICloudStackCoreConnectionFactory _connFactory;
private readonly IMemoryCache _cache;
private readonly ILogger<CheckDatabaseHealthBackgroundService> _logger;
public CheckDatabaseHealthBackgroundService(
IServiceProvider provider,
IJobUtility jobUtility,
IVdcUnitOfWork unitOfWork,
ICloudStackCoreConnectionFactory connFactory,
IMemoryCache cache,
ILogger<CheckDatabaseHealthBackgroundService> logger)
{
_provider = provider;
_jobUtility = jobUtility;
_unitOfWork = unitOfWork;
_connFactory = connFactory;
_cache = cache;
_logger = logger;
}
but after I launch the API the following Exception is thrown:
System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: CloudStackData.WebAPI.RefreshDiskTypeBackgroundService': Cannot consume scoped service 'Vdc.Libs.Data.IVdcUnitOfWork' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.
System.InvalidOperationException: Cannot consume scoped service 'Vdc.Libs.Data.IVdcUnitOfWork' from singleton 'Microsoft.Extensions.Hosting.IHostedService'
After reading about this error I concluded I should add the CheckDatabaseHealthBackgroundService
required services as singleton, not scoped, and so I tried:
Program.cs:
builder.Services.AddScoped<IHypervisorRepository, HypervisorRepository>();
builder.Services.AddScoped<IAccountRepository, AccountRepository>();
builder.Services.AddScoped<ICloudStackDbUnitOfWork, CloudStackDbUnitOfWork>();
builder.Services.AddScoped<ICloudStackCoreConnectionFactory, MySqlCloudStackCoreConnectionFactory>();
builder.Services.AddSingleton<IJobUtility, JobUtility>();
builder.Services.AddSingleton<IVdcUnitOfWork, VdcUnitOfWork>();
builder.Services.AddSingleton<IMemoryCache, MemoryCache>();
builder.Services.AddHostedService<CheckDatabaseHealthBackgroundService>();
And now the Exception thrown is:
System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: CloudStackData.WebAPI.RefreshDiskTypeBackgroundService': Cannot consume scoped service 'Vdc.Libs.Data.DbContextWrite' from singleton 'Vdc.Libs.Data.IVdcUnitOfWork'.
And here I started to be stuck.
VdcUnitOfWork
is a custom class we created:
public sealed class VdcUnitOfWork : EFDbUnitOfWork<DbContextWrite>, IVdcUnitOfWork, IUnitOfWork, IDisposable, IDbContextWrapper
{
public VdcUnitOfWork(DbContextWrite dbContext)
: base(dbContext)
{
}
}
And DbContextWrite
is another custom class:
public abstract class DbContextWrite : DbContext
{
protected DbContextWrite([NotNull] DbContextOptions options)
: base(options)
{
}
}
The fact is after adding the hosted service with AddHostedService
I cannot run the API anymore (I have four hosted services, but just put one for the sake of simplification).
For using scoped-Services in a HostedService
, which is a singleton, you need to inject the IServiceScopeFactory
(using primary constructors from .NET 8.0
):
public sealed class CheckDatabaseHealthBackgroundService
(IServiceScopeFactory scopeFactory) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// get scoped services:
using var scope = scopeFactory.CreateScope();
var jobUtility = scope.ServiceProvider.GetService<IJobUtility>();
var unitOfWork = scope.ServiceProvider.GetService<IVdcUnitOfWork>();
var memoryCache = scope.ServiceProvider.GetService<IMemoryCache>();
// Use services here
}
}