воскресенье, 19 апреля 2026 г.

Comet, CustomBackgroundService01, PeriodicTimer

Comet, CustomBackgroundService01, PeriodicTimer

D:\Projects\VS\2604\Comet\WebAppBackGround01\CustomBackgroundService01\CustomBackgroundService01.csproj

https://www.perplexity.ai/search/asp-netcore-10-10-eto-kod-back-OPgvRKhKSiiglS2QaR5RDA#1

Ниже — пример полноценного CustomBackgroundService : BackgroundService для ASP.NET Core 10 (Razor Pages), который использует всю основную функциональность базового класса и демонстрирует типичный сценарий с PeriodicTimer.

3. Что именно использует CustomBackgroundService из базового класса

  • ExecuteAsync — основной цикл работы сервиса, стартует при старте хоста.

  • ExecuteTask — свойство, которое возвращает Task всей жизни сервиса (можно, например, логировать/отслеживать).

  • StartAsync / StopAsync — базовый класс уже связывает их с жизненным циклом хоста, мы лишь добавили логирование.

  • Dispose — базовый Dispose корректно завершит ресурсы при остановке; мы добавили лог.

  • PeriodicTimer внутри ExecuteAsync — это современный рекомендованный способ периодических задач внутри BackgroundService.


-----------------------------------------------------------------------------------------------------


ExecuteTask — это внутренняя задача, которая представляет «всю жизнь» твоего фонового сервиса: он запускает ExecuteAsync(...) и хранит результат этого вызова как Task, который возвращается через свойство ExecuteTask.

В твоём коде:

csharp
public override Task? ExecuteTask => base.ExecuteTask;

ты просто проксируешь это свойство из базового класса в свой наследуемый сервис. Это не делает ничего «магического» само по себе, но позволяет:

  • обращаться к customService.ExecuteTask извне (например, из Program.cs или тестов) и видеть, какая задача сейчас «живёт» внутри сервиса;

  • знать, когда фоновый сервис завершил свою работу (через await service.ExecuteTask).


Как это «задействовано» в наследуемом сервисе

Посмотрим, что происходит внутри BackgroundService (по сути то же, что в исходниках):

  • В StartAsync базовый класс делает что‑то такое:

    csharp
    _executingTask = ExecuteAsync(_stoppingCts.Token);

    и сохраняет эту задачу в ExecuteTask.

  • В StopAsync базовый класс делает:

    csharp
    await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));

    то есть ждёт либо завершения ExecuteAsync, либо принудительной остановки.

Когда ты пишешь:

csharp
public override Task? ExecuteTask => base.ExecuteTask;

ты:

  • не меняешь поведение ExecuteTask;

  • лишь открываешь его наружу для своего класса, если захочешь потом где-то так использовать:

csharp
var service = scope.ServiceProvider.GetRequiredService<CustomBackgroundService>(); await service.ExecuteTask; // дожидаемся полного завершения фонового сервиса

Простой пример задействования извне

Представь, что ты хочешь в Program.cs (или в тесте) дождаться, пока сервис завершится:

csharp
using var host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddHostedService<CustomBackgroundService>(); }) .Build(); await host.StartAsync(); // Получаем сервис и дожидаемся полного завершения его ExecuteAsync var backgroundService = host.Services.GetRequiredService<CustomBackgroundService>(); if (backgroundService.ExecuteTask is Task t) { // Можно, например, логировать, что сервис завершился await t; Console.WriteLine("CustomBackgroundService завершил работу."); }

То есть ExecuteTask — это не «магия внутри сервиса», а экспорт задачи, порождённой ExecuteAsync, чтобы ты мог:

  • мониторить/логировать;

  • дожидаться завершения;

  • отслеживать ошибки (t.Exception и т.п.).

Если ты сейчас нигде в своём сервисе не используешь ExecuteTask, то переопределение вида

csharp
public override Task? ExecuteTask => base.ExecuteTask;

было сделано только ради демонстрации принципа; фактически BackgroundService корректно работает и без такого явного override.


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

Отправить комментарий