Introduction
In this blog post, we'll explore how to build a .NET Core web application that includes background services. Background services are long-running tasks that run independently of the main application thread, making them ideal for tasks like monitoring, scheduling, and other asynchronous operations. We'll specifically focus on creating two background services: InstrumentationService
and PeriodicService
.
The InstrumentationService
will run continuously, while the PeriodicService
will run periodically based on a configurable interval.
Setting up the Project
Let's start by creating a new .NET Core web application project. Open your terminal or command prompt and run the following command:
dotnet new web -n BackgroundServicesDemo
cd BackgroundServicesDemo
This command creates a new .NET Core web application project named BackgroundServicesDemo
. Navigate into the project directory.
Add appSettings.json modifications
"PeriodicServiceSettings": {
"IntervalInMinutes": 5
}
Creating Background Services
Now, let's create the background services. We'll start by creating the InstrumentationService
class, which will continuously log information.
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;
public class InstrumentationService : IHostedService, IDisposable
{
private readonly ILogger<InstrumentationService> _logger;
private Timer _timer;
public bool IsRunning { get; private set; }
public InstrumentationService(ILogger<InstrumentationService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Instrumentation Service is starting.- {0}", DateTime.Now);
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.Zero); // Adjust delay as needed
IsRunning = true;
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Instrumentation Service is running.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Instrumentation Service is stopping.- {0}", DateTime.Now);
_timer?.Change(Timeout.Infinite, 0);
IsRunning = false;
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
Next, let's create the PeriodicService
class, which will run periodically based on a configurable interval.
public class PeriodicService : BackgroundService
{
private readonly ILogger<PeriodicService> _logger;
private readonly IConfiguration _configuration;
private readonly InstrumentationService _instrumentationService;
private bool _isRunning;
public PeriodicService(
ILogger<PeriodicService> logger,
IConfiguration configuration,
InstrumentationService instrumentationService)
{
_logger = logger;
_configuration = configuration;
_instrumentationService = instrumentationService;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var intervalInMinutes = _configuration.GetValue<int>("PeriodicServiceSettings:IntervalInMinutes");
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Periodic Service is running. - {0}", DateTime.Now);
// Stop Instrumentation Service
_logger.LogInformation("Stopping Instrumentation Service... - {0}", DateTime.Now);
await StopInstrumentationService();
// Do Periodic Service Work
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); // Simulated work
_logger.LogInformation("Periodic Service work is done. - {0}", DateTime.Now);
// Restart Instrumentation Service
_logger.LogInformation("Restarting Instrumentation Service... - {0}", DateTime.Now);
await StartInstrumentationService();
// Wait for next interval
await Task.Delay(TimeSpan.FromMinutes(intervalInMinutes), stoppingToken);
}
}
private async Task StartInstrumentationService()
{
if (InstrumentationServiceIsRunning())
return;
await _instrumentationService.StartAsync(CancellationToken.None);
}
private async Task StopInstrumentationService()
{
if (!InstrumentationServiceIsRunning())
return;
await _instrumentationService.StopAsync(CancellationToken.None);
}
private bool InstrumentationServiceIsRunning()
{
return _instrumentationService.IsRunning; // Placeholder, replace with actual logic
}
}
Registering Background Services
Now, let's register the background services in the dependency injection container. Open the Program.cs
file and update the Main
method as follows:
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddHostedService<InstrumentationService>(); // Register InstrumentationService
builder.Services.AddHostedService<PeriodicService>(); // Register PeriodicService
builder.Services.AddLogging(); // Add logging
// Register InstrumentationService as a singleton
builder.Services.AddSingleton<InstrumentationService>();
await using var app = builder.Build();
// Configure the HTTP request pipeline.
app.MapGet("/", () => "Hello World!");
await app.RunAsync();
}
}
With this setup, the InstrumentationService
will run continuously, and the PeriodicService
will run periodically based on the configured interval.
Conclusion
In this blog post, we explored how to build a .NET Core web application with background services. We created two background services: InstrumentationService
and PeriodicService
, and registered them in the dependency injection container. Background services are a powerful feature of .NET Core, enabling developers to perform asynchronous tasks efficiently.