Skip to content

Background Services

A background service runs independently of the request-response HTTP lifecycle.

Wisp injects dependencies and takes care of managing your service's lifecycle for you.

Writing a Background Service

Warning

Background services are typically registered as singletons. Do not inject scoped services directly into a background service. If you need scoped dependencies (e.g., a database context), create a scope manually using IServiceProvider.CreateScope() inside RunAsync.

public class DatabaseBackgroundService(IServiceProvider serviceProvider)
: IBackgroundService
{
    public async Task RunAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            await using var scope = serviceProvider.CreateAsyncScope();
            var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

            await db.DoWorkAsync(cancellationToken);

            await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
        }
    }
}

A background service must implement the IBackgroundService interface. Any constructor arguments will be injected.

public class HelloWorldBackgroundService : IBackgroundService 
{
    private readonly HelloWorldService _hws;

    public HelloWorldBackgroundService(HelloWorldService hws)
    {
        // Injected from DI
        _hws = hws;
    }

    public async Task RunAsync(CancellationToken cancellationToken) 
    {
        while(!cancellationToken.IsCancellationRequested) 
        {
            await _hws.SayHelloWorldAsync();
            await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
        }
    }
}

If you pass the CancellationToken to awaited operations (such as Task.Delay), the loop will exit automatically when cancellation is requested. Explicitly catching TaskCanceledException is usually not required.

Long-Running Services

Wisp instantiates your service and calls RunAsync once during application startup. The method runs in the background and does not block the HTTP server from starting. It will then cancel the CancellationToken when the application is exiting.

If your service needs to perform continuous work, use a loop that runs until the cancellation token is triggered.

Registering a Background Service

Use the AddBackgroundService<T> extension method.

var hostBuilder = new WispHostBuilder();

hostBuilder.AddBackgroundService<HelloWorldBackgroundService>();

var appBuilder = hostBuilder.Build();
var app = appBuilder.Build();

await app.RunAsync();

Starting Background Services

Background services start automatically on application start.

Stopping Background Services

Wisp will automatically cancel the CancellationToken passed to RunAsync when the application is shutting down.

If your background service coordinates other long-running operations, you should pass the provided CancellationToken to those operations so they can shut down gracefully.

Passing a custom CancellationToken to RunAsync is not currently supported.