Running Background Tasks In ASP.NET Core

Milan Jovanović - Sep 16 '23 - - Dev Community

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);
}
Enter fullscreen mode Exit fullscreen mode

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:

builder.Services.AddHostedService<MyBackgroundTask>();
Enter fullscreen mode Exit fullscreen mode

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 =
            scope.ServiceProvider.GetRequiredService<AppDbContext>();

        await dbContext.Database.MigrateAsync(stoppingToken);
    }
}
Enter fullscreen mode Exit fullscreen mode

The EF DbContext is a scoped service, which we can't inject directly inside of RunEfMigrationsBackgroundTask. We have to inject an instance ofIServiceProvider 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");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

We're using a PeriodicTimerto 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.


P.S. Whenever you're ready, there are 2 ways I can help you:

  1. Pragmatic Clean Architecture: This comprehensive course 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. Join 950+ students here.

  2. Patreon Community: Think like a senior software engineer with access to the source code I use in my YouTube videos and exclusive discounts for my courses. Join 820+ engineers here.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .