Published

- 3 min read

Implementing Long-Running Background Tasks in ASP.NET Core

img of Implementing Long-Running Background Tasks in ASP.NET Core

Implementing Long-Running Background Tasks in ASP.NET Core

Long-running background tasks are essential for performing operations that are not time-sensitive and can be executed asynchronously without blocking the main application thread.

In this article, we will explore how to implement long-running background tasks in ASP.NET Core using hosted services and background workers. We will cover the following topics:

  1. Why Use Background Tasks?
  2. Implementing Background Tasks with IHostedService
  3. Using BackgroundService for Simplicity
  4. Handling Errors and Graceful Shutdown
  5. Real-World Examples

1. Why Use Background Tasks?

Background tasks are useful for performing operations such as:

  • Sending emails
  • Processing data
  • Generating reports
  • Cleaning up resources
  • Running scheduled jobs

By offloading these tasks to background workers, you can improve the responsiveness and performance of your web application.

2. Implementing Background Tasks with IHostedService

The IHostedService interface provides a way to implement background tasks in ASP.NET Core. It defines two methods: StartAsync and StopAsync.

   public class LongRunningService : IHostedService
{
    private readonly ILogger<LongRunningService> _logger;
    private Timer _timer;

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

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Long Running Service is starting.");
        _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        _logger.LogInformation("Long Running Service is working.");
        // Perform long-running task here
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Long Running Service is stopping.");
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }
}

3. Using BackgroundService for Simplicity

The BackgroundService class is a base class for implementing long-running background tasks. It provides a simpler way to create background workers by overriding the ExecuteAsync method.

   public class BackgroundWorker : BackgroundService
{
    private readonly ILogger<BackgroundWorker> _logger;

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

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Background Worker is starting.");

        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Background Worker is working.");
            // Perform long-running task here
            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
        }

        _logger.LogInformation("Background Worker is stopping.");
    }
}

4. Handling Errors and Graceful Shutdown

It’s important to handle errors and ensure a graceful shutdown of background tasks. You can use try-catch blocks and the stoppingToken to handle cancellations.

   protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    _logger.LogInformation("Background Worker is starting.");

    try
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Background Worker is working.");
            // Perform long-running task here
            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "An error occurred while executing the background task.");
    }
    finally
    {
        _logger.LogInformation("Background Worker is stopping.");
    }
}

5. Real-World Scenarios

Sending Emails

You can use a background worker to send emails asynchronously.

   public class EmailSenderService : BackgroundService
{
    private readonly ILogger<EmailSenderService> _logger;
    private readonly IEmailSender _emailSender;

    public EmailSenderService(ILogger<EmailSenderService> logger, IEmailSender emailSender)
    {
        _logger = logger;
        _emailSender = emailSender;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Email Sender Service is starting.");

        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Sending emails.");
            await _emailSender.SendPendingEmailsAsync();
            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }

        _logger.LogInformation("Email Sender Service is stopping.");
    }
}

Data Processing

You can use a background worker to process data in the background.

   public class DataProcessingService : BackgroundService
{
    private readonly ILogger<DataProcessingService> _logger;
    private readonly IDataProcessor _dataProcessor;

    public DataProcessingService(ILogger<DataProcessingService> logger, IDataProcessor dataProcessor)
    {
        _logger = logger;
        _dataProcessor = dataProcessor;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Data Processing Service is starting.");

        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Processing data.");
            await _dataProcessor.ProcessDataAsync();
            await Task.Delay(TimeSpan.FromMinutes(10), stoppingToken);
        }

        _logger.LogInformation("Data Processing Service is stopping.");
    }
}

Implementing long-running background tasks in ASP.NET Core using hosted services and background workers can significantly improve the performance and responsiveness of your web application. By offloading non-time-sensitive operations to background workers, you can ensure that your main application thread remains responsive to user requests.

References