Search code examples
c#.netasp.net-corebackground-servicecancellation-token

Why doesn't Background Service stop in C#?


I'm trying manually stop the class that inherit from BackgroundService.

ExampleService.cs

public class ExampleService : BackgroundService, IExampleService
{
    private readonly ILogger<ExampleService> _logger;
    private bool stopRequested { get; set; }

    public ExampleService(ILogger<ExampleService> logger)
    {
        _logger = logger;
    }
    public async Task Stoping(CancellationToken token)
    {
        _logger.LogInformation("Stoping");
        stopRequested = true;
        await StopAsync(token);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var count = 0;
        while (!stoppingToken.IsCancellationRequested && !stopRequested)
        {
            await Task.Delay(1000, stoppingToken);
            _logger.LogInformation("Service is working {0}", count++);
        }
    }
}
public interface IExampleService
{
    Task Stoping(CancellationToken token = default);

}

Call from API

[HttpGet("stoptask2")]
public async Task<IActionResult> Get()
{
    await exampleService.Stoping();
    return Ok();
}

But the service doesn't stop. I know IHostApplicationLifetime is working but all application is stopping. I don't want this.

I have tried How to cancel manually a BackgroundService in ASP.net core but it doesn't work.

I know I should stop the service using stoppingToken in ExecuteAsync, but how?


Solution

  • Without seeing the actual registration I can only guess, but I think problem is the registration - AddHostedService creates one service descriptor and when you register IExampleService it will create another unrelated one, so you have two different instances of service. Personally I would just use the separation of concerns principle and introduce separate service like IStopper which would have two methods Stop and IStopped (or expose a cancelation toke instead of the second one), but if you want to keep your current class/interface structure - then be sure to provide registration which will result in only one instance:

    builder.Services.AddSingleton<ExampleService>(); // singleton
    builder.Services.AddSingleton<IExampleService>(s => s.GetRequiredService<ExampleService>()); // reabstract as interface
    builder.Services.AddHostedService<ExampleService>(s => s.GetRequiredService<ExampleService>()); // reabstract as hosted service