Что такое .NET Generic Host — основа для DI, конфигурации и логирования
· Го Комура · C#, .NET, Generic Host, Worker, Проектирование
Когда начинаешь писать консольное приложение или worker на .NET, поначалу достаточно немного кода в Main.
Но стоит приложению чуть подрасти, как обычно начинает прибавляться вот что.
- Хочется читать
appsettings.json - Хочется переопределять значения через переменные окружения
- Хочется выводить логи через
ILogger - Не хочется, чтобы создание сервисов превращалось в сплошную цепочку
new - Хочется крутить цикл в фоне
- Хочется корректно завершать работу по
Ctrl+Cили при остановке службы
Вот тут и появляется Generic Host.
Правда, само это название легко перепутать с чем-то другим.
- В чём разница между
Host.CreateApplicationBuilderиHost.CreateDefaultBuilder? - Это то же самое, что DI-контейнер, или нет?
- Как это связано с
BackgroundService? - Это что-то отдельное от
WebApplicationBuilderв ASP.NET Core? - Стоит ли использовать это в консольном приложении?
Когда всё это путается, Generic Host начинает казаться то «чем-то вроде специально для веб-приложений», то, наоборот, «тем, во что нужно оборачивать вообще всё». Оба взгляда довольно поверхностны.
В этой статье, ориентируясь в основном на текущую практику .NET 6 и более поздних версий, сначала разберём следующие четыре вопроса.
- Что такое Generic Host на самом деле
- За что именно он берёт на себя ответственность
- Как связаны между собой
Host.CreateApplicationBuilder,Host.CreateDefaultBuilderиWebApplication.CreateBuilder - С чего лучше начать, чтобы не усложнять себе жизнь
Содержание
- Сначала вывод (в двух словах)
- Сводные таблицы для начала
- 2.1. Что входит в Generic Host
- 2.2. Различия между builder’ами
- Общая картина Generic Host (диаграмма)
- Что даёт Generic Host
- 4.1. Логика запуска собирается в одном месте
- 4.2. DI, конфигурация и логирование связаны с самого начала
- 4.3. Легко управлять корректным завершением и резидентной работой
- Минимальная конфигурация
- 5.1. Минимальный пример для консольного приложения
- 5.2.
appsettings.json - 5.3. Добавляем
BackgroundService
- Типичные паттерны
- 6.1. Недолговечные консольные утилиты
- 6.2. worker / фоновые службы
- 6.3. Он также работает под капотом ASP.NET Core
- Случаи, где он хорошо подходит
- Случаи, где он не подходит или избыточен
- Типичные подводные камни
- Итог
- Источники
1. Сначала вывод (в двух словах)
- Generic Host — это основа, которая объединяет запуск и время жизни .NET-приложения.
- В неё входят DI, конфигурация, логирование,
IHostedService/BackgroundServiceи обработка завершения работы приложения. - В новом не-веб-приложении естественно начинать с
Host.CreateApplicationBuilder(args). WebApplicationBuilderиз ASP.NET Core — не отдельный мир, а тот же самый подход к host, расширенный под задачи веба.- Иными словами, Generic Host — это не просто разговор о DI-контейнере, а механизм, объединяющий точку сборки приложения и управление его жизненным циклом.
Проще говоря, как только приложение выходит за рамки сценария «прочитать аргументы, один раз вывести результат и завершиться», Generic Host начинает давать заметный эффект. И наоборот, привносить его в каждый небольшой инструмент, который до этого уровня ещё не дорос, вовсе не обязательно.
2. Сводные таблицы для начала
2.1. Что входит в Generic Host
Сначала стоит разложить по полочкам, что лежит внутри этой коробки, — так будет заметно проще.
| Элемент | За что отвечает Generic Host | В чём польза |
|---|---|---|
| DI | Собирает сервисы из IServiceCollection |
Легче избавиться от цепочек new |
| Configuration | Объединяет appsettings.json, переменные окружения, аргументы командной строки и т.д. |
Легко обрабатывать различия между окружениями |
| Logging | Создаёт основу для использования ILogger<T> |
Легко позже сменить назначение логов |
| Hosted service | Управляет запуском и остановкой IHostedService / BackgroundService |
Легко отделить резидентную обработку от тела приложения |
| Lifetime | Управляет запуском и остановкой через IHostApplicationLifetime, IHostEnvironment и др. |
Легко унифицировать способ завершения работы при Ctrl+C, SIGTERM или остановке службы |
Здесь важно понимать: Generic Host — это не «просто удобная обёртка над DI». На деле надёжнее воспринимать его как коробку, которая разом собирает всю проводку вокруг точки входа в приложение.
2.2. Различия между builder’ами
Здесь тоже быстрее сразу взглянуть на одну таблицу.
| Точка входа | Основное назначение | Стиль написания | Первый выбор |
|---|---|---|---|
Host.CreateApplicationBuilder(args) |
Новые не-веб-приложения вроде консольных или worker | Пишем напрямую в builder.Services / builder.Configuration / builder.Logging |
Для нового проекта — это |
Host.CreateDefaultBuilder(args) |
Существующий код или конфигурация, построенная в основном на старых методах-расширениях | Строим цепочку вызовов вроде ConfigureServices |
Если есть существующие наработки — это |
WebApplication.CreateBuilder(args) |
Веб-приложения / API на ASP.NET Core | Точка входа Generic Host, дополненная веб-специфичными возможностями | Для веба — это |
CreateApplicationBuilder и CreateDefaultBuilder — это не история про то,
что один из них новая функция, а другой — нечто иное.
Оба обладают одинаковым базовым функционалом и одинаковым поведением по умолчанию. Различие в основном в стиле написания кода.
Для нового не-веб-приложения сегодня естественно начинать с Host.CreateApplicationBuilder(args).
А WebApplication.CreateBuilder(args) проще воспринимать как ту же логику, расширенную под задачи веба, — так проще во всём разобраться.
3. Общая картина Generic Host (диаграмма)
Если в общих чертах изобразить это на диаграмме, получится вот так.
flowchart LR
Args["args / переменные окружения / appsettings.json"] --> Builder["Host.CreateApplicationBuilder(args)"]
Builder --> Config["builder.Configuration"]
Builder --> Services["builder.Services"]
Builder --> Logging["builder.Logging"]
Services --> Hosted["IHostedService / BackgroundService"]
Builder --> Build["builder.Build()"]
Build --> Host["IHost"]
Host --> Run["Run / RunAsync"]
Run --> Lifetime["Запуск / остановка / Ctrl+C / SIGTERM"]
Lifetime --> Hosted
Обычно builder создают в Program.cs,
добавляют сервисы в builder.Services,
при необходимости настраивают builder.Configuration и builder.Logging,
а в конце вызывают Build(), получают IHost и запускают его через Run() / RunAsync().
Незаметный, но существенный момент в том, что уже на этапе вызова Host.CreateApplicationBuilder(args) подключено немало возможностей.
По умолчанию, например, включено следующее.
- Корневой каталог содержимого — текущий каталог
- Конфигурация хоста берётся из переменных окружения с префиксом
DOTNET_и аргументов командной строки - Конфигурация приложения берётся из
appsettings.json,appsettings.{Environment}.json, user secrets в окружении Development, переменных окружения и аргументов командной строки - Логирование выводится в Console / Debug / EventSource / EventLog (только для Windows)
- В окружении
Developmentвключены проверка областей видимости (scope) и проверка зависимостей
То есть речь не о том, что вы бездумно собираете всё с нуля, — с самого начала уже заложена основа, «которой вполне достаточно для обычного использования».
4. Что даёт Generic Host
4.1. Логика запуска собирается в одном месте
Самый незаметный, но при этом самый весомый эффект от Generic Host — точка входа приложения перестаёт расползаться.
Когда приложение чуть подрастает, вокруг Main обычно накапливается примерно следующее.
- Чтение файлов конфигурации
- Подмена значений в зависимости от окружения
- Инициализация логгера
- Сборка
HttpClient, репозиториев и сервисов - Запуск фоновой обработки
- Уборка ресурсов при получении сигнала завершения
Если соединять всё это вручную без host, поначалу может быть легко, но со временем точка входа постепенно становится всё более вязкой.
С Generic Host Program.cs чётко превращается в «место, где разом собираются зависимости».
Уже одно только это наведение порядка заметно облегчает код-ревью.
4.2. DI, конфигурация и логирование связаны с самого начала
С Generic Host DI, конфигурация и логирование с самого начала стоят на одной и той же основе.
Например, на стороне класса можно как само собой разумеющееся получать вот что.
ILogger<T>IConfigurationIHostEnvironmentIOptions<T>
Здесь важно то, что способ чтения конфигурации и способ создания сервисов реже расходятся в разные стили.
Если настроек одна-две, достаточно напрямую читать IConfiguration["Section:Key"] — это тоже работает.
Но когда в реальном проекте настроек становится больше, спокойнее объединять их по секциям в классы через IOptions<T>.
Точно так же с логированием: вместо того чтобы вручную создавать ILoggerFactory в разных местах,
внедрение ILogger<T> в нужные классы делает код более прозрачным.
Удобство Generic Host в том, что он не разносит всё это по отдельным историям, а обрабатывает всё вместе, как единую основу приложения.
4.3. Легко управлять корректным завершением и резидентной работой
Generic Host заботится не только о том, «как запуститься», но и о том, «как остановиться».
Когда host запускается, вызывается StartAsync каждого зарегистрированного IHostedService.
В worker-службах выполняется ExecuteAsync hosted-сервисов, включая BackgroundService.
«Корректное завершение» здесь означает не резкое обрывание обработки, а завершение в следующем порядке:
- передать сигнал остановки
- выйти из циклов и ожиданий
- освободить соединения и ресурсы
Для долго работающих приложений это очень важно.
События вроде Ctrl+C, SIGTERM или остановки службы позволяют легко унифицировать способ завершения работы всего приложения.
Кроме того, когда приложение само хочет запросить завершение работы, можно использовать IHostApplicationLifetime.StopApplication().
Он позволяет подать сигнал «работа уже закончена, пожалуйста, корректно завершись» прямо в контексте host.
5. Минимальная конфигурация
5.1. Минимальный пример для консольного приложения
Первое, что важно понимать: использование Generic Host вовсе не означает,
что обязательно нужно создавать BackgroundService.
Даже для консольной утилиты, которая запускается один раз, Generic Host вполне применим, если нужны DI, конфигурация и логирование.
Чтобы добавить его в обычный консольный проект впоследствии, сначала подключите пакет Microsoft.Extensions.Hosting.
dotnet add package Microsoft.Extensions.Hosting
Минимальный пример Program.cs выглядит, например, так.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<JobRunner>();
using IHost host = builder.Build();
try
{
JobRunner runner = host.Services.GetRequiredService<JobRunner>();
await runner.RunAsync();
return 0;
}
catch (Exception ex)
{
ILogger logger = host.Services
.GetRequiredService<ILoggerFactory>()
.CreateLogger("Program");
logger.LogError(ex, "Unhandled exception occurred during job execution.");
return 1;
}
internal sealed class JobRunner(
ILogger<JobRunner> logger,
IConfiguration configuration,
IHostEnvironment hostEnvironment)
{
public Task RunAsync()
{
string message = configuration["Sample:Message"] ?? "(no message)";
logger.LogInformation("Environment: {EnvironmentName}", hostEnvironment.EnvironmentName);
logger.LogInformation("Message: {Message}", message);
return Task.CompletedTask;
}
}
Если приложение не остаётся резидентным надолго, необязательно доходить до RunAsync().
Можно вызвать Build(), получить нужные сервисы, выполнить работу и просто завершиться.
Даже в этом случае преимущества Generic Host используются в полной мере.
Это на удивление важный момент. Нет необходимости каждый раз тащить шаблон Worker даже в недолговечные задачи.
5.2. appsettings.json
Для приведённого выше примера файла конфигурации в такой минимальной форме вполне достаточно.
{
"Sample": {
"Message": "hello from Generic Host"
}
}
В этом примере значение configuration["Sample:Message"] читается напрямую.
Если нужно посмотреть всего одно-два значения, этого достаточно.
Но когда в реальном проекте настроек становится больше, стоит склоняться к следующему подходу — это помогает избежать разбросанных по коду строковых ключей:
- разделять по классам для каждой секции
- внедрять через
IOptions<T> - проверять при запуске
Кроме того, в настройках Generic Host по умолчанию подключён не только appsettings.json,
но и appsettings.{Environment}.json, переменные окружения и аргументы командной строки,
так что сценарии «подменять значения только при разработке» и «переопределять их через переменные окружения в продакшене» реализуются вполне естественно.
5.3. Добавляем BackgroundService
Для долго выполняющейся обработки использование BackgroundService — довольно естественное решение.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddScoped<PollingJob>();
builder.Services.AddHostedService<PollingWorker>();
using IHost host = builder.Build();
await host.RunAsync();
internal sealed class PollingWorker(
IServiceScopeFactory scopeFactory,
ILogger<PollingWorker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using PeriodicTimer timer = new(TimeSpan.FromSeconds(30));
while (await timer.WaitForNextTickAsync(stoppingToken))
{
using IServiceScope scope = scopeFactory.CreateScope();
PollingJob job = scope.ServiceProvider.GetRequiredService<PollingJob>();
await job.RunAsync(stoppingToken);
logger.LogInformation("Polling completed.");
}
}
}
internal sealed class PollingJob(ILogger<PollingJob> logger)
{
public Task RunAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Do work here.");
return Task.CompletedTask;
}
}
В этом примере стоит обратить внимание на два момента.
- Тело
BackgroundService— этоExecuteAsync - Если нужны scoped-зависимости, создавайте scope через
IServiceScopeFactory
У самого BackgroundService нет области видимости (scope) по умолчанию.
Если, например, нужно использовать scoped-сервис вроде DbContext,
безопасный вариант — разрешать сервис для задачи внутри scope, как показано выше.
Отдельная тема — выбор самого инструмента для периодического выполнения,
но если писать в стиле async, PeriodicTimer — довольно спокойный вариант.
Это перекликается со связанной статьёй про таймеры.
6. Типичные паттерны
6.1. Недолговечные консольные утилиты
Generic Host прекрасно подходит и для приложений, которые выполняют работу один раз и завершаются, — таких как пакетные задания, инструменты конвертации, команды обслуживания.
Он хорошо подходит в таких сценариях.
- Нужно читать файлы конфигурации
- Нужно выводить логи
- Нужно внедрять
HttpClientили репозитории - Нужно возвращать код завершения
Если в подобном приложении сразу же тащить BackgroundService и RunAsync(),
получится немного тяжеловесно и избыточно с точки зрения использования управления жизненным циклом host.
Для недолговечной задачи достаточно просто получить JobRunner и выполнить его, как в предыдущем минимальном примере.
6.2. worker / фоновые службы
Для резидентных worker’ов, опроса (polling), потребления очередей, мониторинга и периодически выполняемых задач
сочетание Generic Host и BackgroundService — довольно естественное решение.
Особенно ценно вот что.
- Логика запуска и остановки унифицирована на стороне host
- Логирование, конфигурация и DI доступны с самого начала
- Легко пробрасывать отмену по
Ctrl+Cили сигналу остановки - Легко отделить тело резидентной обработки от
Program.cs
Кроме того, это легко связать с контекстом Windows Service или контейнеров. Если приложение планируется развивать как резидентное, Generic Host — вполне естественная основа.
При превращении приложения в Windows Service меньше неприятностей возникает, если искать файлы
не исходя из текущего каталога, а отталкиваться от IHostEnvironment.ContentRootPath.
Это потому, что «базовый путь приложения» определяется именно в контексте host.
6.3. Он также работает под капотом ASP.NET Core
В веб-приложениях / API используется WebApplication.CreateBuilder(args),
поэтому на первый взгляд может показаться, что это отдельный от Generic Host мир.
Но по сути они тесно связаны.
builder.Servicesbuilder.Configurationbuilder.Logging
Именно поэтому стиль написания кода получается похожим.
В ASP.NET Core запуск HTTP-сервера тоже входит в жизненный цикл (lifetime) host.
То есть понимание Generic Host помогает и в том смысле, что при чтении веб-версии Program.cs становится понятнее, «почему здесь вообще идёт работа с DI, конфигурацией и логированием».
7. Случаи, где он хорошо подходит
Перечислим ситуации, где Generic Host обычно ложится особенно органично.
- Консольные приложения, использующие конфигурацию, логирование и DI
- worker’ы вроде потребителей очередей, поллеров, watchdog’ов, планировщиков
- Долго работающие приложения, которым нужна уборка ресурсов по
Ctrl+Cили SIGTERM - Приложения, которые в будущем могут вырасти в Windows Service или резидентный контейнер
- Приложения, которые хочется выстроить в том же стиле расширений, что и ASP.NET Core
Их объединяет одно: желание не относиться небрежно к точке входа приложения и управлению его жизненным циклом.
8. Случаи, где он не подходит или избыточен
И наоборот, есть ситуации, где Generic Host необязательно делать главным действующим лицом с самого начала.
- Небольшие инструменты, которые один раз читают аргументы, один раз выводят результат и завершаются
- Наспех написанный проверочный код, используемый лишь несколько десятков минут
- Библиотечные проекты
- Случаи, когда нужно прочитать всего одну настройку, а DI, логирование и управление жизненным циклом не требуются
Здесь более простая реализация без развёртывания host будет спокойнее.
Важно понимать: то, что Generic Host мощный инструмент, не делает его обязательным для абсолютно каждого исполняемого файла.
9. Типичные подводные камни
Напоследок соберём моменты, на которые легко наступить при первом знакомстве с Generic Host.
- Воспринимать Generic Host только как DI-контейнер
- На деле это основа, включающая запуск, остановку, конфигурацию, логирование и hosted service.
- По инерции начинать новое приложение с
Host.CreateDefaultBuilder- Если нет причины подстраиваться под существующий код, для начала естественнее выбрать
Host.CreateApplicationBuilder.
- Если нет причины подстраиваться под существующий код, для начала естественнее выбрать
- Внедрять scoped-сервис напрямую в
BackgroundService- У hosted service нет области видимости (scope) по умолчанию. Безопаснее создавать scope через
IServiceScopeFactory.
- У hosted service нет области видимости (scope) по умолчанию. Безопаснее создавать scope через
- Worker, который должен завершиться после одного запуска, но не сообщает об этом host
- Если реализовывать «run once» через шаблон Worker, но не вызвать
IHostApplicationLifetime.StopApplication()по завершении работы, host продолжит работать и дальше.
- Если реализовывать «run once» через шаблон Worker, но не вызвать
- Хотеть корректного завершения, но обрывать работу через
Environment.Exit- Если вы используете host, для аккуратной остановки правильнее использовать
StopApplication().
- Если вы используете host, для аккуратной остановки правильнее использовать
- Полагаться на текущий каталог (current directory) в Windows Service
- Поиск файлов стабильнее строить, отталкиваясь от
IHostEnvironment.ContentRootPath.
- Поиск файлов стабильнее строить, отталкиваясь от
- Оборачивать недолговечный CLI в
BackgroundServiceс самого начала- Для одноразовой работы достаточно просто получить обычный класс-сервис и выполнить его.
- Небрежно использовать callback-таймер для периодического выполнения в
BackgroundService- Если писать в стиле
async,PeriodicTimerобычно читается лучше и реже приводит к неразберихе.
- Если писать в стиле
В случае с Generic Host достаточно с самого начала разделить, «это недолговечная задача или резидентная», чтобы заметно реже сомневаться в выборе.
10. Итог
Одной фразой: Generic Host — это основа, объединяющая точку входа .NET-приложения и управление его жизненным циклом.
Ещё раз пройдёмся по ключевым моментам.
- Generic Host включает не только DI, но и конфигурацию, логирование, обработку завершения работы и hosted service
- Для нового не-веб-приложения естественный выбор — начать с
Host.CreateApplicationBuilder(args) - Для недолговечной задачи можно обойтись без
BackgroundService, просто выполнив build и запуск - Для резидентной обработки
BackgroundServiceвместе с управлением жизненным циклом (lifetime) host дают заметный эффект - У
BackgroundServiceнет области видимости (scope) по умолчанию, поэтому для scoped-сервисов нужно явно создавать scope WebApplicationBuilderиз ASP.NET Core тоже опирается на ту же концептуальную логику
Generic Host — не инструмент для тяжеловесных ритуалов. Как только конфигурация, логирование, зависимости, запуск и завершение работы начинают хоть немного разрастаться, это инструмент, который не даёт им расползтись по стенам, а собирает их у входа.
И наоборот, для небольших инструментов, которым это пока не нужно, привносить его не обязательно. Как только вы научитесь это различать, Generic Host перестаёт быть чем-то, что «добавляют на всякий случай», и становится практической основой с чётко определённой сферой применения.
11. Источники
- .NET Generic Host - .NET
- Worker Services in .NET
- Use scoped services within a BackgroundService - .NET
- Configuration in .NET
- Options pattern in .NET
- .NET Generic Host in ASP.NET Core
- Create Windows Service using BackgroundService - .NET
- Связанная статья: как выбирать между PeriodicTimer, System.Threading.Timer и DispatcherTimer — сначала разбираемся с периодическим выполнением в .NET
- Связанная статья: лучшие практики C# async/await — таблица решений для Task.Run и ConfigureAwait
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Зачем использовать .NET Generic Host и BackgroundService в десктопных приложениях
Разбираем, как использовать Generic Host и BackgroundService, чтобы упорядочить запуск, периодическую обработку, завершение работы, логир...
Как выбрать межпроцессное взаимодействие в Windows — таблица решений: именованные каналы / TCP / gRPC / разделяемая память / COM
Разбираем, как выбрать способ взаимодействия Windows-приложений друг с другом: сильные стороны и ловушки именованных каналов, локального ...
Как создавать и эксплуатировать службы Windows — от выбора между планировщиком заданий и службами до превращения BackgroundService в службу Windows
Разбираем, стоит ли превращать резидентную обработку в службу Windows или достаточно планировщика заданий: таблица решений, создание служ...
Как выбрать место хранения данных Windows-приложения — таблица решений для SQLite / JSON / реестра / Access
Где и в каком формате хранить данные Windows-приложения. Разбираем выбор между AppData и ProgramData, а также сильные стороны и подводные...
Что такое .NET Native AOT — чем он отличается от JIT и trimming
Разбираемся, что такое Native AOT, через различия с JIT, ReadyToRun, self-contained, single-file, trimming и source generator, и с практи...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Generic Host и архитектура приложения
Generic Host, BackgroundService, DI, конфигурация, журналирование и жизненный цикл приложения.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Речь идёт о построении Windows-приложений с резидентной обработкой, завершением работы, логированием и конфигурацией, поэтому как практическая задача эта тема хорошо сочетается с разработкой Windows-приложений.
Технические консультации и ревью дизайна
Если на этапе до реализации нужно разобраться с DI, временем жизни объектов и разделением ответственности, мы можем начать с прояснения направления в формате технической консультации и ревью архитектуры.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Что такое Generic Host?
- Это основа, которая объединяет запуск и время жизни .NET-приложения. В неё входят DI, конфигурация (Configuration), логирование, IHostedService / BackgroundService и обработка завершения работы приложения. Это не просто обёртка над DI-контейнером — надёжнее воспринимать её как механизм, который объединяет точку сборки приложения и управление его жизненным циклом. Она особенно эффективна в приложениях, где хоть немного разрастаются конфигурация, логирование, зависимости, запуск и завершение работы.
- Что выбрать — Host.CreateApplicationBuilder или Host.CreateDefaultBuilder?
- Для нового не-веб-приложения естественно начинать с Host.CreateApplicationBuilder(args). Оба варианта обладают одинаковым базовым функционалом и одинаковым поведением по умолчанию — речь не о том, что один из них новая функция, а другой нечто иное. Различие в основном в стиле написания кода: CreateApplicationBuilder пишет напрямую в builder.Services и подобные свойства, а CreateDefaultBuilder выстраивает цепочку вызовов вроде ConfigureServices. Если есть причина подстроиться под существующий код или конфигурацию, построенную в основном на старых методах-расширениях, выбирайте CreateDefaultBuilder.
- Есть ли смысл использовать Generic Host в консольном приложении?
- Если нужны DI, конфигурация и логирование, Generic Host вполне пригоден даже для консольной утилиты, которая запускается один раз. Создавать BackgroundService вовсе не обязательно: можно вызвать Build(), получить нужные сервисы и просто завершить работу после выполнения задачи — преимущества Generic Host сохраняются и в этом случае. И наоборот, для небольших инструментов, которые один раз читают аргументы, один раз выводят результат и завершаются, а также для наспех написанного проверочного кода, это избыточно, так что привносить его каждый раз обязательно не нужно.
- Как использовать scoped-сервисы в BackgroundService?
- У BackgroundService нет области видимости (scope) по умолчанию, поэтому напрямую внедрять scoped-сервис через конструктор небезопасно. Безопасный способ — внедрить IServiceScopeFactory, явно создать scope внутри ExecuteAsync и уже в его рамках получать сервисы для задачи. Об этом особенно важно помнить, если нужно использовать scoped-сервис вроде DbContext.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.
Публичные ссылки