Implementing Scheduled Background Tasks in ASP.NET Core with IHostedService
By FoxLearn 12/19/2024 9:45:18 AM 291
In this guide, we will walk you through the process of implementing background tasks using IHostedService
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 theDoWork
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.
- Content Negotiation in Web API
- How to fix 'InvalidOperationException: Scheme already exists: Bearer'
- How to fix System.InvalidOperationException: Scheme already exists: Identity.Application
- Add Thread ID to the Log File using Serilog
- Handling Exceptions in .NET Core API with Middleware
- InProcess Hosting in ASP.NET Core
- Limits on ThreadPool.SetMinThreads and SetMaxThreads
- Controlling DateTime Format in JSON Output with JsonSerializerOptions