Skip to main content

Fixing Incorrect Scope Injection of Services in .Net Core

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

Incorrect Dependency Injection

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.

  1. 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

Popular posts from this blog

Design Application using Clean Architecture

  Clean Architecture , introduced by  Robert C. Martin   (Uncle Bob), aims to create systems that are: Independent of frameworks Testable Independent of UI Independent of database Independent of any external agency Clean Architecture emphasizes separating the concerns of different parts of the system and ensuring that each part has a clear responsibility. .NET clean architecture as a development approach, available in a layered form to reduce dependency. The architecture consists of several layers, each with a specific role and dependencies that flow in one direction: from the outer layers to the inner layers. Inner most Layer is independent to its outer layer, means inner most layer does not have any references, it builds and stand independently. Dependency Rule The core principle of Clean Architecture is the  Dependency Rule , which states that source code dependencies can only point inward.  This means: Nothing in inner circle can depend on anything in an out...

Fixing High CPU Usage in .NET Core using Parallelism

  We will fix problem occurs due to incorrect use Parallelism in .NET Core To increase application performance sometimes we use process task or work extensively in Parallel and that may lead performance issue. When we do code, we should understand our requirement very carefully along with hardware resources those we have. We should code like that there should be less CPU usage and server should work without issues. In .NET Core, when you’re using parallelism (like Parallel.For, Task.WhenAll, or Task.Run), you might encounter performance issues if the number of concurrent threads or tasks exceeds what your system can efficiently handle. A common issue is high CPU usage or resource contention, which can degrade overall performance, cause excessive context switching, and even lead to thread starvation or deadlocks. Non members can access from here Effective Parallelism Strategies to Mitigate High CPU Usage in .NET Core We will fix problem occurs due to incorrect use Parallelism in .NE...

Architecting Resilient .NET Core Applications: Handling Transient Failures.

  Transient Failures happen when server is temporarily overloaded.  We need to handle this instead of sending response back to client and say like that “Server is not responding” In the world of Cloud there are temporary failures that may occur in a system and can be recovered from by retrying the operation. T hese failures typically occur due to network issues, temporary unavailability of external services, timeouts, or rate limiting, and they often resolve themselves after a brief period. Possible Reasons for Transient Failures Resource overloaded:   For example, in high-traffic scenarios, limited system resources (like memory or CPU) can lead to failures when trying to access those resources. Network Issues :  Temporary issues like DNS resolution errors, dropped connections, or network congestion can cause operations to fail. Service Unavailability :  External services or databases might be down or unavailable temporarily.  For example, a database connec...