'How to restart manually a BackgroundService in ASP.net core

I create the BackgroundService:

public class CustomService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        //...
    }
}

and I added to the project:

public class Startup
{
    //...

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHostedService<CustomService>();

        //...
    }

    //...
}

How can I find the CustomService from another class?
How to start it again?



Solution 1:[1]

Create an interface just for the call to StartAsync:

public interface ICustomServiceStarter
{
    Task StartAsync(CancellationToken token = default);
}

public class CustomService : BackgroundService, ICustomServiceStarter
{
    //...
    Task ICustomServiceStarter.StartAsync(CancellationToken token = default) => base.StartAsync(token);
    //...
}

Register the interface as a singleton:

public class Startup
{
    //...
    public void ConfigureServices(IServiceCollection services)
    {
        //...
        services.AddSingleton<ICustomServiceStarter, CustomService>();
    }
    //...
}

and inject ICustomServiceStarter when needed:

public class MyServiceControllerr : Controller
{
    ICustomServiceStarter _starter;

    public MyServiceController(ICustomServiceStarter starter)
    {
        _starter = starter;
    }

    [HttpPost]
    public async Task<IActionResult> Start()
    {
        await _starter.StartAsync();

        return Ok();
    }
}

Solution 2:[2]

When it comes to controller's action, using "await BackgroundService.StartAsync" is the wrong way for long-running tasks.

For instance, the main ussue could be request's timeout depended on proxy settings.

Here is an example how to make your BackgroundService restartable.

BackgroundService implementation:

public class MyBackgroundService: BackgroundService
{
  private volatile bool _isFinished = false;
  private SemaphoreSlim _semaphore = new SemaphoreSlim(0,1);

  protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  {
    _isFinished = false;
     // DoSomeWork
    _isFinished = true;
    await WaitToRestartTask(stoppingToken);
  }

  private async Task WaitToRestartTask(CancellationToken stoppingToken)
  {
     // wait until _semaphore.Release()
     await _semaphore.WaitAsync(stoppingToken);
     // run again
     await base.StartAsync(stoppingToken);
  }

  public void RestartTask()
  {
     if (!_isFinished)
          throw new InvalidOperationException("Background service is still working");

     // enter from _semaphore.WaitAsync
     _semaphore.Release();
  }  
}

Controller's action (for instance):

public async Task<IActionResult> Restart()
{
    var myBackgroundService= _serviceProvider.GetServices<IHostedService>()
                .OfType<MyBackgroundService>()
                .First();

    try
    {
       myBackgroundService.RestartTask();
       return Ok($"MyBackgroundService was restarted");
    }
    catch (InvalidOperationException exception)
    {
       return BadRequest(exception.Message);
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mehdi
Solution 2 Dmitry Nartub