Ответ
Предпочтительный способ — использование IHostedService и его удобной абстракции BackgroundService. Они интегрированы в систему зависимостей и жизненный цикл хоста приложения, что обеспечивает корректный запуск и graceful shutdown.
1. Реализация через BackgroundService (рекомендуется для большинства задач):
public class TimedBackgroundService : BackgroundService
{
private readonly ILogger<TimedBackgroundService> _logger;
public TimedBackgroundService(ILogger<TimedBackgroundService> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Background Service запущен.");
// Периодическая фоновая задача
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));
try
{
while (await timer.WaitForNextTickAsync(stoppingToken))
{
// Работа, выполняемая каждые 5 секунд
_logger.LogInformation("Выполняется фоновая задача в: {time}", DateTimeOffset.Now);
// await DoWorkAsync(stoppingToken);
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("Timed Background Service остановлен.");
}
}
}
2. Регистрация в DI-контейнере:
// В файле Program.cs
builder.Services.AddHostedService<TimedBackgroundService>();
Ключевые преимущества этого подхода:
- Управление жизненным циклом: Сервис автоматически запускается при старте приложения и останавливается при его завершении.
- Graceful Shutdown: Параметр
stoppingTokenавтоматически отменяется при попытке остановки хоста, давая сервису время на корректное завершение операций. - Внедрение зависимостей: Полноценная поддержка DI, можно инжектировать любые зарегистрированные сервисы (репозитории, клиенты API, логгеры).
- Асинхронность: Базовая реализация асинхронна.
Альтернативы и когда их использовать:
IHostedService(прямая реализация): Если нужен более тонкий контроль над процессом запуска/остановки.- Специализированные библиотеки:
- Hangfire: Для сложных фоновых заданий с планировщиком, повторными попытками, панелью мониторинга.
- Quartz.NET: Для высокоточного планирования по cron-выражениям.
TimerилиTask.Runв контроллере: Антипаттерн. Задачи могут быть потеряны при перезапуске приложения, сложно управлять, нет graceful shutdown.
Важные моменты для BackgroundService:
- Переопределяйте
ExecuteAsyncдля основной логики. - Всегда используйте переданный
stoppingTokenдля проверки отмены в долгих циклах или асинхронных операциях. - Обрабатывайте исключения внутри сервиса, чтобы одно упавшее фоновое задание не привело к падению всего хоста приложения.