Implementing Scheduled Background Tasks in ASP.NET Core with IHostedService

By FoxLearn 12/19/2024 9:45:18 AM   291
Scheduled background tasks in ASP.NET Core are essential for tasks that run independently of user interactions, such as sending emails, data processing, or performing system maintenance.

In this guide, we will walk you through the process of implementing background tasks using IHostedService in ASP.NET Core.

background task in asp.net core

Understanding Background Tasks in ASP.NET Core

ASP.NET Core offers two main methods to implement background tasks:

  • Hosted Services (IHostedService): This interface allows tasks to run in the background, independent of the main application pipeline. Hosted services can start automatically when the app begins and can be configured to run continuously, on demand, or at specified intervals.
  • Task Scheduling Libraries: Libraries like Quartz.NET provide more advanced scheduling options, including the ability to execute tasks based on custom triggers, dates, and intervals.

In this tutorial, we will focus on implementing background tasks using the IHostedService interface, utilizing a simple Timer to set time intervals for task execution.

Start by creating a new ASP.NET Core project.

Implementing a Background Task Using IHostedService

The IHostedService interface defines two key methods:

  • StartAsync: This method runs when the application starts.
  • StopAsync: This method is triggered when the application is shutting down.

Create a class called TimedHostedService that implements IHostedService and handles the background task execution.

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

public class TimedHostedService : IHostedService, IDisposable
{
    private readonly ILogger<TimedHostedService> _logger;
    private Timer _timer;

    public TimedHostedService(ILogger<TimedHostedService> logger)
    {
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is starting.");

        // Schedule the background task to run every 5 minutes
        _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));

        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        _logger.LogInformation("Timed Background Service is working. {Time}", DateTimeOffset.Now);
        // Add your background task code here, such as sending an email or processing data.
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is stopping.");

        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}

In this example:

  • Timer: The _timer schedules the DoWork method to run at specified intervals (5 minutes in this case).
  • Logger: The _logger logs service status information, helpful for monitoring the service.

To ensure the background task runs when the application starts, you need to register the hosted service in the Program.cs or Startup.cs file.

For ASP.NET Core 6+:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);

// Register the background service
builder.Services.AddHostedService<TimedHostedService>();

var app = builder.Build();
app.Run();

For earlier versions of ASP.NET Core, register the service in ConfigureServices inside Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHostedService<TimedHostedService>();
}

Run the application, and you will see log messages indicating the background task has started and is working. The DoWork method will log a message every 5 minutes, confirming that the task is running as expected.

In real-world scenarios, background tasks often need to interact with other services, such as a database or external APIs. ASP.NET Core's dependency injection makes it easy to integrate these dependencies into your background tasks.

To inject dependencies into your background service, modify the constructor of TimedHostedService to accept services, and register them in Program.cs or Startup.cs:

public class TimedHostedService : IHostedService, IDisposable
{
    private readonly ILogger<TimedHostedService> _logger;
    private readonly IServiceProvider _serviceProvider;
    private Timer _timer;

    public TimedHostedService(ILogger<TimedHostedService> logger, IServiceProvider serviceProvider)
    {
        _logger = logger;
        _serviceProvider = serviceProvider;
    }

    private void DoWork(object state)
    {
        using (var scope = _serviceProvider.CreateScope())
        {
            var myDependency = scope.ServiceProvider.GetRequiredService<MyDependency>();
            myDependency.ExecuteTask();
        }

        _logger.LogInformation("Timed Background Service is working. {Time}", DateTimeOffset.Now);
    }
}

In this example:

  • IServiceProvider: This allows the background service to access other services.
  • CreateScope: Creates a new scope for the background task, ensuring each task instance has its own dependencies without interfering with others.

For more complex scheduling needs, consider using Quartz.NET, an open-source library that offers advanced scheduling capabilities.

To implement Quartz.NET, first install the Quartz.NET package using the command:

dotnet add package Quartz.Extensions.Hosting

You can configure Quartz by using the AddQuartzHostedService extension method on IServiceCollection. This adds a hosted Quartz server that starts and stops according to the application's lifetime.

public class Program
{
    public static void Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .Enrich.FromLogContext()
            .WriteTo.Console()
            .CreateLogger();
        
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseSerilog()
            .ConfigureServices((hostContext, services) =>
            {
                // see Quartz.Extensions.DependencyInjection documentation about how to configure different configuration aspects
                services.AddQuartz(q =>
                {
                    // your configuration here
                });

                // Quartz.Extensions.Hosting hosting
                services.AddQuartzHostedService(options =>
                {
                    // when shutting down we want jobs to complete gracefully
                    options.WaitForJobsToComplete = true;
                });
            });
}

By implementing background tasks using IHostedService in ASP.NET Core, you can easily handle recurring operations in the background, such as sending notifications or cleaning up resources, without blocking user requests.