Background tasks run behind the scenes without any user intervention. It could be triggered based on time or some other external events. We can use background tasks in many places. For examples:

  • Clean up database or file system every day
  • Perform some high CPU intensive work asynchronously
  • Process messages from a queue every X minutes
  • Refresh cache every X minutes
  • Send payslip to employees every month
  • Send performance reports to stakeholders every day

Hosted Service

A hosted service is a class that contains background task logic that implements directly from an IHostedService interface or a BackgroundService abstract class.

However, we can also create background tasks without using a hosted service. So the next question is why I should use hosted service to create background tasks. The answer is simple:

  • The hosted service execution is coordinated with the lifetime of the application
  • And, it allows doing graceful clean-up when the application is shutting down.

In general, we can colocate the hosted services along with web applications. However, if we want to execute the background tasks out of the web application, we can use the WorkerService template.

Configuration

The hosted service is registered within the familiar ConfigureServices method. In the below code, the DbMigrationHostedService will start first, then it will kick start the RefreshCacheHostedService, and then it will start the Kestral web server.

public class Startup
{
  ...
  public void ConfigureServices(IServiceCollection services)
  {
    services.AddHostedService<DbMigrationHostedService>();
    services.AddHostedService<RefreshCacheHostedService>();
  }
}

However, if we want to run the Kestral web server before starting the hosted services, then we can register the hosted service in Program.cs:

public class Program
{
  ...
public static IHostBuilder CreateHostBuilder(string[] args) =>
  Host.CreateDefaultBuilder(args)
      .ConfigureWebHostDefaults(webBuilder =>
      {
          webBuilder.UseStartup<Startup>();
      })
      .ConfigureServices(services =>
      {
          services.AddHostedService<DbMigrationHostedService>();
          services.AddHostedService<RefreshCacheHostedService>();
      });
    }
}

Cancellation Token

The default cancellation token timeout is 5 seconds in the .NET Core application, so any remaining operations after the default timeout will be aborted. To achieve the graceful shutdown, we need to execute the stopAsync within 5 seconds. However, sometimes it is truly impossible to do the cleanup operation within 5 seconds. In such cases, you can change the default shutdown timeout limit:

public class Program
{
  ...
  public static IHostBuilder CreateHostBuilder(string[] args) =>
      Host.CreateDefaultBuilder(args)
          .ConfigureWebHostDefaults(webBuilder =>
          {
              webBuilder.UseStartup<Startup>();
          })
          .ConfigureServices(services =>
          {
              services.Configure<HostOptions>(option =>
              {
                  option.ShutdownTimeout = TimeSpan.FromSeconds(15);
              });
              ...
          });
}

Here is the youtube link of my presentation at Azure community meetup, Singapore, where I explained different options available for creating background tasks in detail:

Background Tasks

And, here is the source code link for the same: GitHub