Веб-API ASP.NET Core 8: невозможно зарегистрировать службу с ограниченной областью из синглтона

В моем 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 (у меня четыре размещенных сервиса, но ради упрощения я поставил только один).

Любая помощь?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Для использования сервисов с ограниченной областью действия в 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
    }
}

Вы должны понимать, что синглтон живет дольше, поэтому для получения необходимых сервисов вам необходимо контролировать область действия, а это означает, что в тот момент времени, когда они вам нужны, создайте область действия, или ваша единица работы может создать свою собственную конкретную область действия. в зависимости от вашей реализации. Эта проблема часто возникает, когда вы смешиваете фоновые рабочие процессы с логикой API или WFE. Альтернативой является изменение других служб на синглтоны, но обычно это не очень хорошая идея.

Chris Schaller 01.07.2024 17:25

@ChrisSchaller Спасибо за дополнительную информацию. Особенно при использовании EF-Core DbContext и/или UnitOfWork Repos Singleton не является вариантом, и нам в любом случае понадобится контекст с ограниченной областью.

decius 01.07.2024 17:32

Спасибо обоим, @decius и ChrisSchaller, теперь это действительно работает, и мы все каждый день узнаем (или обновляем) что-то новое здесь вместе с вашими друзьями-разработчиками.

Diego Perez 01.07.2024 17:53

Другие вопросы по теме