Передать в BackgroundService параметр

Есть BackgroundService. Как передать в него int-овый параметр? Т.е. в Setup файле получаю из appsettings переменную и хочу её передать сервису при регистрации.

public class TestService : BackgroundSerice
{
   private readonly IServiceProvider _serviceProvider; 
 
   // Нужно передать someValue при регистрации сервиса
   public TestService(IServiceProvider serviceProvider, int someValue)
   {
       _serviceProvider = serviceProvider;
   }

   //...
}

Обычная регистрация выполняется так (если не надо передавать someValue):

builder.Services.AddHostedService<TestService>();

Ответы (2 шт):

Автор решения: Dev18

Если я правильно понимаю, то вот так можно. Я использую статический сервис с настройками appsettings

{
  "MySettings": {
    "SomeValue": 42 //наше int-овое значение
  }
}
namespace VashProject.AppSettings
{
    public static class ApplicationServicesSettings
    {
        public const string MySettings = "MySettings";
        public static MyClassValue MyClassValue { get; set; } = new MyClassValue();
    }

    public class MyClassValue
    {
        public int SomeValue { get; set; }
    }
}

Program

builder.Configuration.GetSection(ApplicationServicesSettings.MySettings)
       .Bind(ApplicationServicesSettings.MyClassValue);

builder.Services.AddHostedService(serviceProvider => 
    new TestService(serviceProvider, ApplicationServicesSettings.MyClassValue.SomeValue));
→ Ссылка
Автор решения: EvgeniyZ

Допустим, у нас есть такой сервис:

public class TestService(IServiceProvider serviceProvider, int value) : IHostedService
{
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        while (true)
        {
            Console.WriteLine(value);
            await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

Наша задача передать в него int value, вопрос.. Как?

Вариантов несколько:

  1. Передать это значение при регистрации, сделав что-то такое:

    builder.Services.AddHostedService(serviceProvider=> new TestService(serviceProvider, 1000));
    

    Тут мы регистрируем не тип, который будет за нас создан, а уже полноценный экземпляр класса, который мы заполняем самостоятельно нужными данными.

    Для красоты, можем сделать свой метод расширения:

    public static class Extensions
    {
        public static IServiceCollection AddTestService(this IServiceCollection services, int value)
        {
            services.AddHostedService(ServiceProvider => new TestService(ServiceProvider, value);
            return services;
        } 
    }
    

    Тогда регистрация будет простой builder.Services.AddTestService(1000);. Если настроек много, то передавайте Action<>, тогда будет стандартное AddTestService (options => { options.Value = ...; })

  2. Мы можем сделать класс, который будет хранить в себе нужные данные и запрашивать через DI уже его. Один из вариантов пусть будет таким:

    public class TestServiceSettings
    {
        public int Value { get; set; } = 1000;
    }
    

    В сервисе запрашиваем этот класс:

    public class TestService(..., TestServiceSettings settings) : IHostedService
    {
        public async Task StartAsync(CancellationToken cancellationToken)
        {
            ...
                Console.WriteLine(settings.Value);
            ...
        }
    
        ...
    }
    

    Ну и регистрация пусть будет такой:

    builder.Services.AddScoped<TestServiceSettings>();
    builder.Services.AddHostedService<TestService>();
    

    Все, дальше за нас все инициализирует и внедрит DI контейнер. Пример конечно так себе, но на деле класс с настройками может работать так, как вам надо, хоть получайте настройки с сервера.

  3. Раз уж затронули настройки, то можно использовать тогда и их.

    • Создаем файл с нужными настройками, допустим, пусть это будет config.json:

       {
         "TestServiceOptions": {
           "SomeInt": 101
         }
       }
      
    • Регистрируем его:

      builder.Configuration.AddJsonFile("config.json");
      
    • Дальше используем в сервисе, пробросив туда зависимость IConfiguration configuration, ну и в нужном месте читаем нужную настройку configuration["TestServiceOptions:SomeInt"].

    • Если нам надо, чтоб данные динамично менялись при изменении настроек в файле, то при регистрации конфигурации устанавливаем reloadOnChange (.AddJsonFile("config.json", true, reloadOnChange: true)).

    • Ну а для удобства работы со всем этим, применяем паттерн настроек (IOptions):

      • Создаем класс, который будет соответствовать настройкам:

         public class TestServiceOptions
         {
             public int SomeInt { get; set; }
         }
        
      • Настраиваем IOptions после регистрации файла:

         builder.Services.Configure<TestServiceOptions>(builder.Configuration.GetSection(nameof(TestServiceOptions)));
        
      • Применяем в сервисе, пробросив туда IOptions<TestServiceOptions> options и в нужном месте options.Value.SomeInt.

      • Но тут учтите, с автоматическим обновлением значения из файла нужно будет повозиться немного, это уже оставлю вам.

Собственно, вот вам 3 разных подхода, берите любой и используйте. Тут конечно есть еще варианты, но это по сути основные.

→ Ссылка