The IHostedService interface is a simple but powerful feature that was introduced in .NET Core 2.0 for managing background tasks in your application. In this post, we’ll take a hands-on look at implementing and using IHostedService to create robust, long-running services in .NET Core.
Overview of IHostedService
IHostedService provides a standard way to write components that integrate with the .NET Core hosting infrastructure. At its core, it defines a single method:
Task StartAsync(CancellationToken cancellationToken);
Any class that implements IHostedService can register itself with the built-in dependency injection container. At startup, the host will call StartAsync on each registered IHostedService to initialize it.
This provides a clean, decoupled way to set up background processing, timers, event listeners, and other long-running logic. The DI container handles the lifetime and disposal of IHostedService instances.
Creating a Simple IHostedService
Let’s look at a basic example of an IHostedService implementation:
public class MyBackgroundTask : IHostedService {
public Task StartAsync(CancellationToken cancellationToken) {
// Implementation logic here
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) {
// Clean up any resources
return Task.CompletedTask;
}
}
We implement the interface and define the StartAsync and StopAsync methods. StartAsync is where we initialize our background task, while StopAsync allows us to clean up resources if the service is stopped gracefully.
Registering IHostedServices
To enable our IHostedService, we need to add it to the dependency injection container in Startup.cs:
public void ConfigureServices(IServiceCollection services) {
services.AddHostedService<MyBackgroundTask>();
}
This registers MyBackgroundTask to be instantiated and managed by the DI container. Its StartAsync method will automatically be called at application startup.
Running Async Logic in StartAsync
Now, let’s make our background task do something useful. Here is an example that processes items from a queue on a timer:
public async Task StartAsync(CancellationToken cancellationToken) {
// Create timer
var timer = new Timer(ProcessQueue, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
await Task.CompletedTask;
}
private void ProcessQueue(object state) {
// Poll queue and process items
}
We create a timer that invokes our queue processing method on an interval. By making StartAsync async and awaiting Task.CompletedTask: this lets the method run indefinitely.
Handling Cancellation
To properly handle shutdowns, we need to monitor cancellationToken in StartAsync:
public async Task StartAsync(CancellationToken cancellationToken) {
var timer = new Timer(ProcessQueue, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(100, cancellationToken);
}
// Stop timer
}
This allows grace to wind down when the token triggers. We would also want to implement StopAsync to cancel the timer if StopAsync is called externally immediately.
With these basics, you can implement reliable, long-running IHostedServices on .NET Core! They provide great flexibility to create autonomous background behavior in your apps.
Common IHostedService Scenarios
Here are some common use cases where IHostedService shines:
- Running timed background jobs like pruning logs/caches
- Processing work queues for resource-intensive tasks
- Implementing a heartbeat or watchdog to monitor system health
- Running scheduled tasks like batch emails
- Integrating with external event sources like message buses
- Encapsulating legacy services that don’t implement modern APIs
IHostedService allows separating these cross-cutting concerns from your core business logic. The DI container handles their lifecycle so they can run independently in the background.
Comparing IHostedService to BackgroundService
In .NET Core 3.0, a new BackgroundService base class was introduced that provides a simpler model for background tasks:
public class MyTask : BackgroundService {
protected override Task ExecuteAsync(CancellationToken stoppingToken) {
// Implement background logic
}
}
BackgroundService derives from IHostedService but handles the boilerplate of implementing StartAsync/StopAsync. In most cases, BackgroundService provides a quicker way to implement background logic. However, IHostedService still has its place where full control over startup/shutdown is needed.
Tips for Effective IHostedServices
Here are some tips for using IHostedService effectively:
- Make services resilient to crashes. Restart automatically if possible.
- Be careful with external dependencies. Configure them to retry and handle failures.
- Use dependency injection rather than direct instantiation. This improves testability and modularity.
- Implement graceful shutdown logic in StopAsync. Don’t start anything that can’t be stopped.
- Consider scoping hosted services as singletons. Shared instances are easier to coordinate.
- Use asynchronous operations and async/await for non-blocking I/O. Don’t block the thread pool.
By following best practices like these, you can build robust hosted services using this straightforward .NET Core interface.
Wrapping Up
IHostedService provides a simple background task abstraction on .NET Core that makes it easy to create decoupled background behavior for common cross-cutting concerns when combined with dependency injection for automatic lifetime management. While approaches like threading or timers could achieve similar goals, IHostedService integrates seamlessly with the ASP.NET Core hosting infrastructure. So if you need robust background tasks or services in your .NET Core application, it's worth considering hiring .NET Core developers familiar with leveraging IHostedService.
Top comments (0)