C#

Validating Services Lifetime in .NET Core: Best Practices Revealed

We should be very careful while doing dependency Injection in .Net core API, else we can face abnormal behavior of application.

Understanding Services Lifetime in .NET Core

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.


line

Copyrights © 2024 letsupdateskills All rights reserved