В моем API я регистрирую некоторые сервисы с помощью AddHostedService
, которым необходимо внедрить в них некоторые интерфейсы, например:
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;
}
но после запуска API выдается следующее исключение:
System.InvalidOperationException: ошибка при проверке дескриптора службы «ServiceType: Microsoft.Extensions.Hosting.IHostedService Срок службы: Singleton ImplementationType: CloudStackData.WebAPI.RefreshDiskTypeBackgroundService»: невозможно использовать ограниченную службу «Vdc.Libs.Data.IVdcUnitOfWork» из синглтона «Microsoft. Расширения.Хостинг.IHostedService».
System.InvalidOperationException: невозможно использовать службу с ограниченной областью действия «Vdc.Libs.Data.IVdcUnitOfWork» из одноэлементного «Microsoft.Extensions.Hosting.IHostedService».
Прочитав об этой ошибке, я пришел к выводу, что мне следует добавить необходимые CheckDatabaseHealthBackgroundService
сервисы как синглтон, а не с ограниченной областью действия, и поэтому я попробовал:
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>();
И теперь выброшенное исключение:
System.InvalidOperationException: ошибка при проверке дескриптора службы «ServiceType: Microsoft.Extensions.Hosting.IHostedService Срок службы: Singleton ImplementationType: CloudStackData.WebAPI.RefreshDiskTypeBackgroundService»: невозможно использовать ограниченную службу «Vdc.Libs.Data.DbContextWrite» из одноэлементного «Vdc. Libs.Data.IVdcUnitOfWork'.
И тут я начал застревать.
VdcUnitOfWork
— это созданный нами пользовательский класс:
public sealed class VdcUnitOfWork : EFDbUnitOfWork<DbContextWrite>, IVdcUnitOfWork, IUnitOfWork, IDisposable, IDbContextWrapper
{
public VdcUnitOfWork(DbContextWrite dbContext)
: base(dbContext)
{
}
}
И DbContextWrite
— еще один пользовательский класс:
public abstract class DbContextWrite : DbContext
{
protected DbContextWrite([NotNull] DbContextOptions options)
: base(options)
{
}
}
Дело в том, что после добавления размещенного сервиса с помощью AddHostedService
я больше не могу запускать API (у меня четыре размещенных сервиса, но ради упрощения я поставил только один).
Любая помощь?
Для использования сервисов с ограниченной областью действия в HostedService
, который является одноэлементным, вам необходимо внедрить IServiceScopeFactory
(используя первичные конструкторы из .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
}
}
@ChrisSchaller Спасибо за дополнительную информацию. Особенно при использовании EF-Core DbContext и/или UnitOfWork Repos Singleton не является вариантом, и нам в любом случае понадобится контекст с ограниченной областью.
Спасибо обоим, @decius и ChrisSchaller, теперь это действительно работает, и мы все каждый день узнаем (или обновляем) что-то новое здесь вместе с вашими друзьями-разработчиками.
Вы должны понимать, что синглтон живет дольше, поэтому для получения необходимых сервисов вам необходимо контролировать область действия, а это означает, что в тот момент времени, когда они вам нужны, создайте область действия, или ваша единица работы может создать свою собственную конкретную область действия. в зависимости от вашей реализации. Эта проблема часто возникает, когда вы смешиваете фоновые рабочие процессы с логикой API или WFE. Альтернативой является изменение других служб на синглтоны, но обычно это не очень хорошая идея.