We should be very careful while doing dependency Injection in .Net core API, else we can face abnormal behavior of application.
Before diving into the dependency injection issues which also known as Captive Dependency issue, let's understand the different dependency lifetimes in .NET Core:
1. Transient: A new instance is created every time it is requested.
2. Scoped: A single instance is created per request (or per scope).
3. Singleton: A single instance is created and shared across the entire application lifecycle.
If a transient service is injected into a singleton, it will be resolved only once when the singleton is created. This leads to unexpected behavior since the transient service should be short-lived
Why Validate Lifetime?
Prevent Captive Dependency - Avoid injecting transient services into singletons.
Ensure Proper Scope Usage - Avoid injecting scoped services into singletons.
Improve Performance & Maintainability - Detect misconfigurations early.
Reduce Memory Leaks - Ensure services are disposed of correctly.
When a transient service is injected into a singleton, the transient service is not recreated for every request. Instead, it is locked inside the singleton for the entire application lifetime. This goes against the transient lifecycle, leading to:
Let's understand problem
1. Consider Below Transient Service.
public class Transient_Service_Logger { public void Log(string message) => Console.WriteLine($"Log: {message}"); }
2. Scope Service
We have injected Transient Service into Scope service (this is incorrect injection done)
public class Scoped_Service { private readonly Transient_Service_Logger _logger; public Scoped_Service(Transient_Service_Logger logger) { _logger = logger; } public void ProcessPayment() { _logger.Log("Processing Payment..."); } }
3. Singelton Service
We have injected Scope service into Singelton Service (this is incorrect injection done)
public class Singelton_Service { private readonly Scoped_Service _paymentProcessor; public Singelton_Service(Scoped_Service paymentProcessor) { _paymentProcessor = paymentProcessor; } public void PlaceOrder() { _paymentProcessor.ProcessPayment(); Console.WriteLine("Order Placed Successfully!"); } }
4. Registration
builder.Services.AddSingleton
(); builder.Services.AddScoped (); builder.Services.AddTransient ();
5. Validation Code For detecting incorrect injection
Before .NET 8.0
We need to validate scopes explicitly because auto detect for incorrect injection feature came in .NET 8.0.
builder.Host.UseDefaultServiceProvider(options => { options.ValidateScopes = true; // Ensure scoped services aren't injected into singletons options.ValidateOnBuild = true; // Validate service configurations during startup });
Code Execution
Once we add code to validate scope as above, we will get below error on starting the application. so as soon we did some mistake, we will be getting error if we added code to validate scopes.
How ValidateScopes = true Works
Prevents scoped dependencies from being injected into singletons.
Helps catch captive dependency issues (e.g., injecting DbContext into a singleton).
How ValidateOnBuild= true Works
Validates service registrations at application startup.
Ensures all dependencies can be resolved before the app starts.
Helps detect missing dependencies and misconfigured lifetimes.
Validation Scope in .NET 8.0
In .NET 8.0 we do not need to add validate scope code as we did above, in .net 8.0 auto detection will be happening if we have some incorrect injection done in application.
Copyrights © 2024 letsupdateskills All rights reserved