#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
...
}
}