.NET Core и C# : IHostedServices не запускаются автоматически без WebHost.Run

#c# #.net-core

Вопрос:

Я написал следующий код на C#, работающий на .NET Core 3.1.

Я пытаюсь придумать более чистый способ сделать это.

IHostedServices не запускается автоматически без webHost.Run

Может ли кто-нибудь предложить?

 public static class Program
{
    public static async Task Main(string[] args)            
    {            
        var webHost = CreateWebHostBuilder(args).Build();  
                  
        if (args.Any(arg => string.Equals(arg, "sqlrate=latest", StringComparison.InvariantCultureIgnoreCase)))            
        {            
            var cancellationToken = new CancellationToken();            
            var sqlWatchers = webHost.Services            
                .GetServices<IHostedService>()            
                .OfType<ISqlCollectionWatcher>()            
                .ToList();

            foreach (var sqlDbWatcher in sqlWatchers)            
            {            
                await sqlDbWatcher.StartAsync(cancellationToken);            
            }
            
            var migrationRunner = webHost.Services.GetService<IMigrationRunner>();            
            var migrationLocator = (MigrationLocator)webHost.Services.GetService<IMigrationLocator>();            
            migrationLocator.Assembly = Assembly.GetExecutingAssembly();            
            var migrationResult = migrationRunner.UpdateToLatest();            
            Console.Write($"Migration result: {migrationResult}"); 
            foreach (var sqlDbWatcher in sqlWatchers)            
            {
                await sqlDbWatcher.StopAsync(cancellationToken);
            }
        }
        else
        {
            webHost.Run();
            await webHost.RunAsync();
        }
    }
    
    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseSerilog((hostingContext, loggerConfiguration) =>
            {
                var isDevelopment = hostingContext.HostingEnvironment.IsDevelopment();
                loggerConfiguration
                    .MinimumLevel.Is(isDevelopment ? LogEventLevel.Debug : LogEventLevel.Information)
                    .MinimumLevel.Override("Microsoft", isDevelopment ? LogEventLevel.Debug : LogEventLevel.Warning)
                    .MinimumLevel.Override("System", isDevelopment ? LogEventLevel.Debug : LogEventLevel.Warning)
                    .ReadFrom.Configuration(hostingContext.Configuration)
                    .WriteTo.Console(new StackdriverJsonFormatter())
                    .Enrich.WithExceptionDetails()
                    .Enrich.FromLogContext()
                    .Enrich.WithOpenTracingContext();
            });
}
 

Комментарии:

1. Это Run() необходимо, потому что оно обеспечивает CancellationToken то, что будет остановлено , когда приложение будет закрыто. И этот токен выдается всем службам и т.д. Поэтому Run() для запуска служб необходим вызов.

2. «Более чистый способ» сделать что именно? Дамп кода без объяснения на самом деле не говорит нам, чего вы на самом деле пытаетесь достичь.

Ответ №1:

Пример в документации Microsoft показывает, что вам нужно выполнить:

 public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}
 

Я предполагаю, что это «ответ из авторитетного источника» 😉

Я пытаюсь придумать более чистый способ сделать это.

Не вызывайте run/runasync дважды

Ответ №2:

Да, это правда. Вам нужен запуск HostedService, и вы можете запустить только один HostedService, поскольку именно так они создаются в библиотеке расширений.

Насколько я понимаю, вы хотите запустить дополнительные службы, если указан определенный параметр. Лучший способ сделать это-инкапсулировать бизнес в другую службу и запустить эту службу в вашей основной службе хостинга. Многим людям нужно что-то подобное, и мы либо создаем отдельные сервисы, либо размещаем их в рамках основного сервиса.

Если вы размещаете его в своей основной службе, у вас также есть преимущество в том, что у вас есть функция отмены, чтобы ваши наблюдатели тоже могли изящно завершить работу.

Вы можете проверить, существует ли аргумент, а затем зарегистрировать свою службу; если нет, то нет.

 ...
services.AddSingleton<DbWatcherManager>();
...


class MainService {
  MainService (DbWatcherManager watchManager){}
  
  async Task ExecuteAsync(CancellationToken stoppingToken){
    
    await watchManager.StartWatchers(stoppingToken);
    ...

    // run some happy code here and
    // beware of dragons

    ...

  }

}