.NET Generic Host & Application Lifetime
Mind Map Summary
- .NET Generic Host (
IHost
)- Definition: A standard, non-HTTP-specific host for .NET applications, responsible for startup and lifetime management.
- Responsibilities:
- Dependency Injection (DI)
- Configuration (e.g.,
appsettings.json
) - Logging
- Application Lifetime Management
- Benefit: Unifies the bootstrapping process for different app types (Worker Services, Web Apps, etc.).
- Application Lifetime (
IHostApplicationLifetime
)- Definition: A service that provides events to hook into the application’s lifecycle.
- Key Events:
ApplicationStarted
: Triggered after the host is fully started. Use for post-startup tasks.ApplicationStopping
: Triggered on graceful shutdown (e.g., Ctrl+C). Use for cleanup.ApplicationStopped
: Triggered after all services have been stopped.
- Background Services (
IHostedService
)- Definition: The primary interface for implementing long-running background tasks within the Generic Host.
- Key Methods:
StartAsync(CancellationToken)
: Called by the host at startup. This is where you start your work (e.g., begin a timer, connect to a message queue).StopAsync(CancellationToken)
: Called by the host during graceful shutdown. This is where you clean up resources and stop your work.
- Registration: Registered in the DI container via
services.AddHostedService<MyService>()
.
Core Concepts
1. The Generic Host
- Purpose: Before .NET Core 3.0, web applications used a
WebHost
and console/background apps had no standardized hosting pattern. The Generic Host (Host.CreateDefaultBuilder
) was introduced to provide a single, consistent way to initialize an application’s infrastructure, regardless of its type. It elegantly handles setting up configuration from various sources, configuring logging providers, and creating the dependency injection container that the application will use.
2. IHostApplicationLifetime
- Purpose: This singleton service is the host’s way of communicating its state to the application. By injecting
IHostApplicationLifetime
into any service, you can register actions to be performed when the application starts, is in the process of stopping, or has fully stopped. This is crucial for tasks that need to happen at specific points, like warming up a cache on startup or flushing logs during shutdown.
3. IHostedService
- Purpose: This is the standard contract for any long-running task that should be managed by the host. The host is responsible for starting and stopping all registered
IHostedService
implementations. - How it Works: At startup, the host iterates through all registered
IHostedService
instances and calls theirStartAsync
methods. They then run in the background for the duration of the application’s life. When a shutdown is triggered (e.g., by pressing Ctrl+C or receiving a stop signal from an orchestrator), the host calls theStopAsync
method on each service, giving it a chance to finish its current work and clean up gracefully before the application exits. BackgroundService
: This is an abstract base class that implementsIHostedService
. It provides a more convenientExecuteAsync
method, which is the recommended starting point for most worker services.
Practice Exercise
Create a .NET Core Worker Service project. Implement a background service (IHostedService
) that logs a message every 5 seconds. Configure it in Program.cs
and explain how the Generic Host manages the startup and graceful shutdown of your service.
Answer
Code Example
1. Create a New Worker Service Project
In your terminal, run: dotnet new worker -o MyWorkerService
2. The Background Service (Worker.cs
)
This file is created by the template. We will modify it to log messages.
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
public override async Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Worker starting at: {time}", DateTimeOffset.Now);
await base.StartAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(5000, stoppingToken);
}
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Worker stopping at: {time}", DateTimeOffset.Now);
await base.StopAsync(cancellationToken);
}
}
3. Configuration (Program.cs
)
The template also creates this file, which sets up the Generic Host.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
// The host registers our Worker class as a hosted service.
services.AddHostedService<Worker>();
});
}
Explanation of Host Management
-
Startup Process:
- When you run the application,
Host.CreateDefaultBuilder(args).Build().Run()
creates and starts the Generic Host. - The host scans the DI container for any registered
IHostedService
implementations. - It finds our
Worker
class (registered viaservices.AddHostedService<Worker>()
). - The host calls the
StartAsync
method on theWorker
instance. Our override logs the “Worker starting” message. - The host then calls the
ExecuteAsync
method. Ourwhile
loop begins, logging a message every 5 seconds.
- When you run the application,
-
Graceful Shutdown Process:
- When you press Ctrl+C in the console, the host intercepts this signal and initiates a graceful shutdown.
- It passes a
CancellationToken
to all hosted services. In ourWorker
, thestoppingToken
passed toExecuteAsync
now hasIsCancellationRequested
set totrue
. - The
while
loop condition becomes false, and the loop terminates cleanly. - The host then calls the
StopAsync
method on theWorker
instance. Our override logs the “Worker stopping” message. - This demonstrates a clean, predictable shutdown, allowing your background tasks to finish their current work and release any resources before the application terminates.