Running Background Tasks In ASP.NET Core

Running Background Tasks In ASP.NET Core

3 min read ·

Thank you to our sponsors who keep this newsletter free to the reader:

QuadSpinner Highlighter is an open-source Visual Studio extension that lets you highlight important objects and arbitrary texts to help you navigate your code more easily.

Sick of staying up late firefighting deployments? Reclaim your nights with a database that keeps your app up even when things go sideways. Outsource hosting to the experts with RavenDB Cloud today so you can sleep well tomorrow.

In this week's newsletter we will talk about running background tasks in ASP.NET Core. After reading this newsletter, you will be able to set up a background task and have it up and running within minutes.

Background tasks are used to offload some work in your application to the background, outside of the normal application flow. A typical example can be asynchronously processing messages from a queue.

I will show you how to create a simple background task that runs once and completes.

And you will also see how to configure a continuous background task, that repeats after a specific period.

Let's dive in.

Background Tasks With IHostedService

You can define a background task by implementing the IHostedService interface. It has only two methods.

Here's what the IHostedService interface looks like:

public interface IHostedService
    Task StartAsync(CancellationToken cancellationToken);

    Task StopAsync(CancellationToken cancellationToken);

All you have to do is implement the StartAsync and StopAsync methods.

Inside of StartAsync you would usually perform the background processing. And inside of StopAsync you would perform any cleanup that is necessary, such as disposing of resources.

To configure the background task you have to call the AddHostedService method:


Calling AddHostedService will configure the background task as a singleton service.

So does dependency injection still work in IHostedService implementations?
Yes, but you can only inject transient or singleton services.

However, I don't like to implement the IHostedService interface myself. I prefer using the BackgroundService class instead.

Background Tasks With BackgroundService

The BackgroundService class already implements the IHostedService interface, and it has an abstract method that you need to override - ExecuteAsync. When you are using the BackgroundService class, you only have to think about the operation you want to implement.

Here's an example background task that runs EF migrations:

public class RunEfMigrationsBackgroundTask : BackgroundService
    private readonly IServiceProvider _serviceProvider;

    public RunEfMigrationsBackgroundTask(IServiceProvider serviceProvider)
        _serviceProvider = serviceProvider;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        using IServiceScope scope = _serviceProvider.CreateScope();

        await using AppDbContext dbContext =

        await dbContext.Database.MigrateAsync(stoppingToken);

The EF DbContext is a scoped service, which we can't inject directly inside of RunEfMigrationsBackgroundTask. We have to inject an instance of IServiceProvider which we can use to create a custom service scope, so that we can resolve the scoped AppDbContext.

I would not recommend running the RunEfMigrationsBackgroundTask in production. EF migrations can easily fail and you'll run into problems. However, I think it's perfectly fine for local development.

Periodic Background Tasks

Sometimes we want run a background task continuously, and have it perform some operation on repeat. For example, we want consume messages from a queue every ten seconds. How do we build this?

Here's an example PeriodicBackgroundTask to get you started:

public class PeriodicBackgroundTask : BackgroundService
    private readonly TimeSpan _period = TimeSpan.FromSeconds(5);
    private readonly ILogger<PeriodicBackgroundTask> _logger;

    public PeriodicBackgroundTask(ILogger<PeriodicBackgroundTask> logger)
        _logger = logger;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        using PeriodicTimer timer = new PeriodicTimer(_period);

        while (!stoppingToken.IsCancellationRequested &&
               await timer.WaitForNextTickAsync(stoppingToken))
            _logger.LogInformation("Executing PeriodicBackgroundTask");

We're using a PeriodicTimer to asynchronously wait for a given period, before executing our background task.

What If You Need A More Robust Solution?

It should be obvious by now that IHostedService is useful when you need simple background tasks that are running while your application is running.

What if you want to have a scheduled background task that runs at 2AM every day?

You can probably build something like this yourself, but there are existing solutions that you should consider first.

Here are two popular solutions for running background tasks that I worked with before:

I also have an example of using Quartz for processing Outbox messages on my YouTube channel that you can take a look at.

Whenever you're ready, there are 4 ways I can help you:

  1. Modular Monolith Architecture (NEW): Join 600+ engineers in this in-depth course that will transform the way you build modern systems. You will learn the best practices for applying the Modular Monolith architecture in a real-world scenario.
  2. Pragmatic Clean Architecture: Join 2,750+ students in this comprehensive course that will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture.
  3. Patreon Community: Join a community of 1,050+ engineers and software architects. You will also unlock access to the source code I use in my YouTube videos, early access to future videos, and exclusive discounts for my courses.
  4. Promote yourself to 51,000+ subscribers by sponsoring this newsletter.

Become a Better .NET Software Engineer

Join 51,000+ engineers who are improving their skills every Saturday morning.