Dependency inject BackgroundService into controllers
By FoxLearn 2/4/2025 8:21:18 AM 8
The reason is that BackgroundService
is intended to run in the background as part of the application's hosted services, and it doesn't follow the typical request/response lifecycle of controllers.
For example, how to inject a DatabaseLoggerService
as a background service while using ILoggerService
for dependency injection in controllers.
We’ll make sure to abstract away the specific implementation of DatabaseLoggerService
from the controller, and ensure that ILoggerService
is used for logging purposes in the controllers.
First, define the DatabaseLoggerService
as a BackgroundService
that implements an ILoggerService
interface.
public interface ILoggerService { Task LogAsync(string message); } public class DatabaseLoggerService : BackgroundService, ILoggerService { private readonly IHostApplicationLifetime _hostApplicationLifetime; private readonly ILogger<DatabaseLoggerService> _logger; public DatabaseLoggerService(IHostApplicationLifetime hostApplicationLifetime, ILogger<DatabaseLoggerService> logger) { _hostApplicationLifetime = hostApplicationLifetime; _logger = logger; } public async Task LogAsync(string message) { // Log message to the database (you can replace this with actual DB logging logic) _logger.LogInformation($"Logging to database: {message}"); await Task.CompletedTask; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { // This is where background work can be handled await Task.Delay(1000, stoppingToken); // Simulate background work } } }
Next, define the controller that will use the ILoggerService
for logging. The controller does not need to know about DatabaseLoggerService
specifically.
[Route("[controller]")] [ApiController] public class RecipesController : ControllerBase { private readonly ILoggerService _logger; public RecipesController(ILoggerService logger) { _logger = logger; } [HttpGet("{id}")] public async Task<IActionResult> Get(int id) { await _logger.LogAsync($"Fetching recipe with ID: {id}"); // Your logic here return Ok(); } }
In Program.cs
(for .NET 6 and later), register the DatabaseLoggerService
as both an ILoggerService
and as a HostedService
. This ensures that the service will run in the background and also be available to controllers via dependency injection.
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) => { // Register DatabaseLoggerService as both ILoggerService and HostedService services.AddSingleton<ILoggerService>(sp => { var hostAppLifetime = sp.GetRequiredService<IHostApplicationLifetime>(); return new DatabaseLoggerService(hostAppLifetime, sp.GetRequiredService<ILogger<DatabaseLoggerService>>()); }); // Register as Hosted Service so it will run in the background services.AddHostedService<DatabaseLoggerService>(); // Add controllers services.AddControllers(); }); }
If DatabaseLoggerService
has dependencies (like IHostApplicationLifetime
), you can resolve them using GetRequiredService()
or GetService()
in the registration process. For example, if DatabaseLoggerService
depends on IHostApplicationLifetime
, you can register it as shown above. This is done by retrieving the required service (IHostApplicationLifetime
) and passing it into the constructor of DatabaseLoggerService
.
Why Abstract the Implementation?
In this setup, the controller doesn't directly interact with the DatabaseLoggerService
implementation. Instead, it interacts with the ILoggerService
interface, which allows you to change the logging mechanism later without changing the controller's code. The controller does not need to know about the background service or its lifecycle.
This follows the dependency inversion principle from SOLID design, where the controller depends on abstractions (interfaces) rather than concrete implementations.
Why Not Inject BackgroundService or the Concrete Class?
Controllers should not be concerned with the internal mechanics of a background service. By injecting the ILoggerService
interface, you allow the service to be replaced with any other logging mechanism in the future (e.g., in-memory logging, file logging, etc.) without changing controller code.
- How to supply IOptions in ASP.NET Core
- Logging requests and responses in ASP.NET Core
- How to manually validate a model in a controller in ASP.NET Core
- How to disable ModelStateInvalidFilter in ASP.NET Core
- How to add custom middleware in ASP.NET Core
- How to Turn Off Startup Logging in ASP.NET Core
- How to Configure JSON Serializer options in ASP.NET Core
- How to use Razor View Engine in ASP.NET Core