How to stop the ASP.NET service when a background service crashes

When a background service throws an exception in ExecuteAsync() and stops running, nothing happens. No errors are logged, and the ASP.NET service continues to run.

What if you want to stop the ASP.NET service when the background service crashes?

To do that, you’ll need to handle exceptions in the background service and call IHostApplicationLifetime.StopApplication(), like this:

public class DatabaseLoggerService : BackgroundService { private IHostApplicationLifetime HostApplicationLifetime; public DatabaseLoggerService(IHostApplicationLifetime hostApplicationLifetime) { HostApplicationLifetime = hostApplicationLifetime; } protected async override Task ExecuteAsync(CancellationToken stoppingToken) { while(!stoppingToken.IsCancellationRequested) { try { //Execute the important background activity in a try/catch await BulkInsert(stoppingToken); } catch(TaskCanceledException canceledEx) { //Service was stopped, so exit the loop return; } catch (Exception fatalException) { //Handle fatal exceptions appropriately based on your requirements //Then initiate a graceful shutdown HostApplicationLifetime.StopApplication(); return; } } } //rest of class }
Code language: C# (cs)

Note: After initiating the shutdown, exit ExecuteAsync() with ‘return’ or ‘throw’. StopApplication() will eventually stop the background service, but it’s not always immediate. This means the loop conditions could still be true, and the loop could continue to execute. Don’t let this be a possibility, exit the method explicitly.

Calling HostApplicationLifetime.StopApplication() initiates a graceful shutdown. The framework will call StopAsync() in all background services you have running.

Don’t call Environment.Exit(). It may appear that it’s doing a graceful shutdown, but it’s not. If you use that, you may run into unexpected problems. Use IHostApplicationLifetime.StopApplication() instead.

Dependency inject IHostApplicationLifetime

You’ll need to dependency inject IHostApplicationLifetime into your background service to be able to use it. IHostApplicationLifetime is registered by the framework, so it’s always available to use.

To dependency inject it, first add it as a constructor parameter in your background service:

public DatabaseLoggerService(IHostApplicationLifetime hostApplicationLifetime)
Code language: C# (cs)

In most scenarios, you won’t need to do anything special. You can just register your background service by using AddHostedService(), and the framework will automatically resolve the IHostApplicationLifetime dependency:

public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddHostedService<DatabaseLoggerService>(); //rest of method } //rest of class }
Code language: C# (cs)

If you need to create and register the background service manually, like if you’re passing it as a reference to the controllers, then you’ll need to resolve the IHostApplicationLifetime service manually, like this:

public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddSingleton<ILoggerService>(sp => { var hostAppLifetime = sp.GetService<IHostApplicationLifetime>(); return new DatabaseLoggerService(hostAppLifetime); }); services.AddHostedService(sp => sp.GetService<ILoggerService>() as DatabaseLoggerService ); //rest of method } //rest of class }
Code language: C# (cs)

Leave a Comment