Sometimes we inject Scope Services into singleton Services which may cause incorrect behavior of application, we will see how we can identify those objects or services

Identifying and resolving issues caused by incorrect scope injection in services is crucial for maintaining the integrity and performance of a .NET Core application. Incorrectly scoped services can lead to unintended behavior, performance issues, or memory leaks.
It is very important to understand Lifetime of object in .NET core and how to register with incorrect lifetime.
Key rules
- Transient services can be injected into any other lifetime.
- Scoped services can be injected into other scoped services, or into transient services, but not into singletons.
- Singleton services can only safely inject other singletons, not scoped or transient services.
Why?
- Scoped services are disposed at the end of a request. Injecting a scoped service into a singleton would mean that the scoped service could be disposed of before the singleton finishes using it.
- Transient services are created each time, so they can be injected into any service without issue, but it’s generally not a good idea to inject them into long-lived singletons.
Example and Implementation
Let’s see a incorrect injection example and how we will catch it.
- Creation of Scope Service.
public interface IScopedService
{
void DoSomeAction();
}
public class ScopedService : IScopedService
{
public void DoSomeAction()
{
Console.WriteLine("Performing scoped action...");
}
}
2. Creation of Singelton Service and scope service injection.
public class MySingletonService
{
private readonly IScopedService _scopedService;
// Constructor injects a Scoped service into a Singleton (incorrect)
public MySingletonService(IScopedService scopedService)
{
_scopedService = scopedService;
}
public void Execute()
{
_scopedService.DoSomeAction();
}
}
3. Code to Catch incorrect scope injections
public static class ServiceCollectionExtensions
{
public static void ValidateServiceLifetimes(this IServiceCollection services)
{
var descriptors = services.ToList(); // Get all registered services
foreach (var descriptor in descriptors)
{
// Check for Singleton services that depend on Scoped or Transient services
if (descriptor.Lifetime == ServiceLifetime.Singleton)
{
var dependentServices = descriptors
.Where(d => d.Lifetime == ServiceLifetime.Scoped || d.Lifetime == ServiceLifetime.Transient)
.ToList();
foreach (var dependentService in dependentServices)
{
// If a Singleton service depends on a Scoped or Transient service, throw an exception
if (dependentService.ServiceType.IsAssignableFrom(descriptor.ServiceType))
{
throw new InvalidOperationException(
$"Service '{descriptor.ServiceType.Name}' with Singleton lifetime " +
$"cannot depend on service '{dependentService.ServiceType.Name}' with {dependentService.Lifetime} lifetime.");
}
}
}
}
}
4. Service Registration at Startup.cs
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<MySingletonService>(); // Singleton
builder.Services.AddScoped<IScopedService, ScopedService>(); // Scoped
// Validate service lifetimes before application starts
builder.Services.ValidateServiceLifetimes();
5. Code execution

we can see , as we injected incorrect scope service into singelton we are getting error as per above screen shot.
Explanation
a. We created Scope Service named as: ScopedService
b. We created Singelton service : MySingletonService
c. We injected IScopedService into MySingletonService which is incorrect.
d. We created extension method ValidateServiceLifetimes on IServiceCollection
e. As per Rule below
Scoped services can be injected into other scoped services, or into transient services, but not into singletons.
we tried to find how many singelton services we have using below code
var descriptors = services.ToList(); // Get all registered services
foreach (var descriptor in descriptors)
{
// Check for Singleton services that depend on Scoped or Transient services
if (descriptor.Lifetime == ServiceLifetime.Singleton
we tried to find if scope service injected into singelton
var dependentServices = descriptors
.Where(d => d.Lifetime == ServiceLifetime.Scoped || d.Lifetime == ServiceLifetime.Transient)
.ToList();
foreach (var dependentService in dependentServices)
{
if such case or service found throw exception
foreach (var dependentService in dependentServices)
{
// If a Singleton service depends on a Scoped or Transient service, throw an exception
if (dependentService.ServiceType.IsAssignableFrom(descriptor.ServiceType))
{
throw new InvalidOperationException(
$"Service '{descriptor.ServiceType.Name}' with Singleton lifetime " +
$"cannot depend on service '{dependentService.ServiceType.Name}' with {dependentService.Lifetime} lifetime.");
}
}
Conclusion
We should follow correct rules for DI else we will get issues like memory leak or sometimes we will get Invalid operation exceptions.
Thanks Happy coding.
#coding
#injection
#dependencyinjection
#programming
#dotnetcore
Comments
Post a Comment