Running Background Processes in .NET 8: Why and How?
Article Context, I was working on an older Windows project whose goal was to make it AKS Linux Compatible, one of the things we had to take into consideration was that this project have a few dependencies because it needs to run background processes.
So, I think this article can be useful like it was for me.
Let's start, As applications grow, the need for efficient background processing becomes increasingly important. Scalability, responsiveness, and resource optimization are key reasons to move specific tasks out of the main application flow. In .NET, especially with .NET 8, there are powerful and flexible ways to run background tasks, allowing certain operations to be handled asynchronously.
Imagine scenarios like these:
In each case, it’s clear that waiting for these tasks to be completed before responding to an API request could slow down the system, harming the user experience. Instead, running such tasks in the background is ideal, keeping the main application flow responsive.
.NET 8 makes it easier to implement this with improved support for background processing, notably through the BackgroundService class and hosted services. Here’s a look at some practical approaches.
Example 1: Using BackgroundService for Background Tasks
The simplest approach to creating background services is to extend the BackgroundService class. Here’s a look at sending emails in the background after an order is placed.
Example Code:
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
public class EmailSenderBackgroundService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// Check if there are pending emails in a queue
var email = await FetchPendingEmailAsync();
if (email != null)
{
await SendEmailAsync(email);
Console.WriteLine("Email sent successfully!");
}
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
}
}
private Task<string> FetchPendingEmailAsync()
{
// Simulate fetching an email from a queue
return Task.FromResult("[email protected]");
}
private Task SendEmailAsync(string email)
{
// Simulate sending an email
Console.WriteLine($"Sending email to {email}");
return Task.CompletedTask;
}
}
In this example, EmailSenderBackgroundService continuously polls for emails in a queue and sends them in the background without blocking the main application thread.
领英推荐
Example 2: Implementing Periodic Tasks with IHostedService
The IHostedService interface is also commonly used for background tasks, especially if you want precise control over start and stop functionality.
Example Code:
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public class CacheCleanupService : IHostedService, IDisposable
{
private readonly ILogger<CacheCleanupService> _logger;
private Timer _timer;
public CacheCleanupService(ILogger<CacheCleanupService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Cache cleanup service starting.");
_timer = new Timer(CleanUpCache, null, TimeSpan.Zero, TimeSpan.FromMinutes(30));
return Task.CompletedTask;
}
private void CleanUpCache(object state)
{
_logger.LogInformation("Cache cleanup in progress.");
// Logic to clear stale cache items
}
public Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Cache cleanup service stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
Example 3: Using QueueBackgroundWorkItem for Short-Lived Tasks
In scenarios where you only need occasional, short-lived background work, .NET 8 provides QueueBackgroundWorkItem. This approach is perfect for low-latency, on-demand tasks.
Example Code:
using Microsoft.Extensions.Hosting;
public class OrderService
{
private readonly IHostApplicationLifetime _appLifetime;
public OrderService(IHostApplicationLifetime appLifetime)
{
_appLifetime = appLifetime;
}
public void ProcessOrder(string orderDetails)
{
_appLifetime.ApplicationStopping.Register(() =>
{
// Here we queue a background task to send confirmation email
QueueBackgroundWorkItem(async token =>
{
await SendOrderConfirmationEmail(orderDetails);
});
});
}
private Task SendOrderConfirmationEmail(string orderDetails)
{
// Simulate sending an order confirmation email
Console.WriteLine($"Order confirmation email sent for {orderDetails}");
return Task.CompletedTask;
}
}
With QueueBackgroundWorkItem, you can queue lightweight, short-lived background tasks as needed without blocking the main thread, keeping your application flow smooth and responsive.
Wrapping Up
Running background tasks in .NET 8 is straightforward and powerful. Depending on the use case, you can leverage BackgroundService, IHostedService, or QueueBackgroundWorkItem to manage long-running or short-lived tasks. This flexibility allows .NET applications to stay performant and responsive, ensuring a smoother experience for your users. Whether sending emails, processing queues, or managing cache, .NET 8 has you covered for effective background processing.