I create a BackgroundService like this:
public class CustomService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
do
{
//...
await Task.Delay(60000, cancellationToken);
}
while (!cancellationToken.IsCancellationRequested);
}
}
How to cancel it manually?
It's unclear whether you want to cancel all services and maybe the application itself (or at least the host), or just a single service.
Stopping the application
To cancel the application, inject the IHostApplicationLifetime interface in the class that will force the cancellation and call StopApplication when needed. If you want to cancel from inside the background service itself, perhaps because there's nothing else to do, that's where you need to inject.
StopApplication
will tell the host the application needs to shut down. The host will call StopAsync
on all hosted services. Since you use BackgroundService
, the implementation will trigger the cancellationToken
passed to ExecuteAsync
:
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executeTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executeTask, Task.Delay(Timeout.Infinite, cancellationToken)).ConfigureAwait(false);
}
}
You don't have to change your current code at all. The only concern is that await Task.Delay()
leaks timers. It would be better to use a Timer
explicitly, and dispose it when cancellation is triggered.
For example, if you want to shut down the application from a controller action:
public class MyServiceController:Controller
{
IHostApplicationLifetime _lifetime;
public MyServiceController(IHostApplicationLifetime lifeTime)
{
_lifeTime=lifeTime;
}
[HttpPost]
public IActionResult Stop()
{
_lifeTime.StopApplication();
return Ok();
}
}
Stopping the service
If you want to stop just this one service, you need a way to call its StopAsync
method from some other code. There are numerous ways to do this. One such way is to inject CustomService
to the caller and call StopAsync
. That's not a very good idea though, as it exposes the service and couples the controller/stopping code with the service. Testing this won't be easy either.
Another possibility is to create an interface just for the call to StopAsync
, eg :
public interface ICustomServiceStopper
{
Task StopAsync(CancellationToken token=default);
}
public class CustomService : BackgroundService,ICustomServiceStopper
{
...
Task ICustomServiceStopper.StopAsync(CancellationToken token=default)=>base.StopAsync(token);
}
Register the interface as a singleton:
services.AddSingleton<ICustomServiceStopper,CustomService>();
and inject ICustomServiceStopper
when needed:
public class MyServiceController:Controller
{
ICustomServiceStopper _stopper;
public MyServiceController(ICustomServiceStopper stopper)
{
_stopper=stopper;
}
[HttpPost]
public async Task<IActionResult> Stop()
{
await _stopper.StopAsync();
return Ok();
}
}