- Home
- .NET tutorials
- Common errors to avoid in ASP.NET Core dependency injection
Common errors to avoid in ASP.NET Core dependency injection
Published: Monday 4 August 2025
There are a number of common errors that you'll want to avoid when using dependency injection in ASP.NET Core.
Failing to register a dependency
When you inject a service, it expects it to be registered in the IoC container.
Take this service:
// ICategoryNotAddedService.cs
public interface ICategoryNotAddedService
{
bool NotAdded();
}
// CategoryNotAddedService.cs
public interface ICategoryNotAddedService
{
public bool NotAdded() => true;
}
We are injecting it into the WebApiNotAddedController
and using it in the NotAdded
endpoint method:
// WebApiNotAddedController.cs
[ApiController]
[Route("api/[controller]")]
public class WebApiNotAddedController : ControllerBase
{
private readonly ICategoryNotAddedService
_categoryNotAddedService;
public WebApiNotAddedController(
ICategoryNotAddedService categoryNotAddedService
)
{
_categoryNotAddedService = categoryNotAddedService;
}
[HttpGet]
public IActionResult NotAdded()
{
return Ok(new
{
NotAdded = _categoryNotAddedService.NotAdded()
});
}
}
However, if we look at Program.cs
, there is no sign of the service being registered:
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddOpenApi();
var app = builder.Build();
...
If we execute the NotAdded
API endpoint method in WebApiNotAddedController
, we'll get the following exception:
InvalidOperationException: Unable to resolve service for type 'ICategoryNotAddedService' while attempting to activate 'WebApiNotAddedController'.
The easy solution for this is to make sure that the service has been registered. In Program.cs
, use either AddSingleton
, AddScoped
or AddTransient
extension methods in builder.Services
to register the service depending on what service lifetime you want to use:
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add this line
builder.Services.AddScoped<ICategoryNotAddedService, CategoryNotAddedService>();
builder.Services.AddControllersWithViews();
builder.Services.AddOpenApi();
var app = builder.Build();
Circular dependency
We have these two classes:
// ProductService.cs
public class ProductService : IProductService
{
private readonly ICategoryService _categoryService;
public ProductService(ICategoryService categoryService)
{
_categoryService = categoryService;
}
}
// CategoryService.cs
public class CategoryService : ICategoryService
{
private readonly IProductService _productService;
public CategoryService(IProductService productService)
{
_productService = productService;
}
}
The ProductService
class that implements the IProductService
interface injects the ICategoryService
service. Whereas, the CategoryService
class that implements the ICategoryService
interface injects the IProductService
service.
And that's where the problem lies. It gets itself into a loop and you'll get the following exception:
AggregateException: A circular dependency was detected for the service
There is no real solution for this. The best thing you can do to avoid this exception is to refactor your code by losing the circular dependency.
Injecting a scoped service lifetime into a singleton
We've registed the CategorySingletonService
class implementation with a singleton service lifetime and the CategoryScopedService
class implementation with a scoped service lifetime.
// Program.cs
builder.Services.AddSingleton<ICategorySingletonService, CategorySingletonService>();
builder.Services.AddScoped<ICategoryScopedService, CategoryScopedService>();
We then attempt to inject the ICategoryScopedService
type into the CategorySingletonService
class:
// CategorySingletonService.cs
public class CategorySingletonService : ICategorySingletonService
{
public DateTime Datestamp { get; }
public CategorySingletonService(ICategoryScopedService categoryScopedService)
{
Datestamp = DateTime.UtcNow;
}
}
However once we start the application, we get this exception:
AggregateException: Cannot consume scoped service 'ICategoryScopedService' from singleton 'ICategorySingletonService'.
This is because the CategorySingletonService
class wouldn't know which scope to resolve.
If you need to use a scoped service lifetime in a singleton service lifetime, you'll need to create a new scope and then reference the instance from that scope.
You could do it by replacing the ICategoryScopedService
injection with injecting the IServiceScopeFactory
type. Then when you need to resolve the ICategoryScopedService
type, you can create a new scope and resolve the ICategoryScopedService
type from that scope.
We've shown an example of how to do it in the GetScopedTime
method:
// CategorySingletonService.cs
public class CategorySingletonService : ICategorySingletonService
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public CategorySingletonService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public DateTime? GetScopedTime()
{
using var scope = _serviceScopeFactory.CreateScope();
var scoped = scope.ServiceProvider.GetRequiredService<ICategoryScopedService>();
return scoped.Datestamp;
}
}
Ambiguous constructors
Take this class:
// CategoryAmbiguousService.cs
public class CategoryAmbiguousService : ICategoryAmbiguousService
{
public CategoryAmbiguousService(ICategorySingletonService categorySingletonService)
{
}
public CategoryAmbiguousService(ICategoryScopedService categoryScopedService)
{
}
}
Both the ICategorySingletonService
and ICategoryScopedService
types have been registered as services:
// Program.cs
builder.Services.AddScoped<ICategoryAmbiguousService, CategoryAmbiguousService>();
builder.Services.AddSingleton<ICategorySingletonService, CategorySingletonService>();
builder.Services.AddScoped<ICategoryScopedService, CategoryScopedService>();
So how does the IoC container know which constructor to use when initalising this service? It doesn't. If the IoC container tried to create a new instance of CategoryAmbiguousService
, it would throw the following exception:
AggregateException: The following constructors are ambiguous
The solution is to either remove one of the constructors. If you are not able to do that, you can use the factory implementation extension method to register the ICategoryAmbiguousService
by specifying the constructor that you wish to use to create the new instance:
// Program.cs
builder.Services.AddScoped<ICategoryAmbiguousService>(serviceProvider =>
{
return new CategoryAmbiguousService(serviceProvider.GetRequiredService<ICategorySingletonService>());
});
This will use the constructor overload that expects the ICategorySingletonService
type.
Sharing scope and transient service lifetimes across multiple threads
If you start sharing scoped and transient service lifetime instances across multiple threads, you are likely to come across issues where the service has already been disposed.
Take this class:
// CategoryScopedService.cs
public class CategoryScopedService : ICategoryScopedService, IDisposable
{
public DateTime? Datestamp { get; private set; }
public CategoryScopedService()
{
Datestamp = DateTime.UtcNow;
}
public void Dispose()
{
Datestamp = null;
}
}
We are implementing the IDisposable
interface and setting the Datestamp
property to null
when the class is disposed.
We then have this web API controller:
// WebApiSharedScopeController.cs
public class WebApiSharedScopeController : ControllerBase
{
private readonly ICategoryScopedService _categoryScopedService;
public WebApiSharedScopeController(ICategoryScopedService categoryScopedService)
{
_categoryScopedService = categoryScopedService;
}
[HttpGet("shared-service")]
public IActionResult SharedService()
{
Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine($"Task.Run Datestamp = {_categoryScopedService.Datestamp?.ToString("HH:mm:ss.fffffff")}");
});
Console.WriteLine($"Controller Datestamp = {_categoryScopedService.Datestamp?.ToString("HH:mm:ss.fffffff")}");
return this.Ok();
}
}
We are sharing the ICategoryScopedService
instance in the SharedService
method but also in a separate Task.Run
method. The task adds a five second delay before writing the Datestamp
property to the console application.
We run the SharedService
method and get a 200 response. But when we view the console application, no datestamp is recorded in the task:
Controller Datestamp = 17:22:09.1019790
Task.Run Datestamp =
The reason being is that the scope that the ICategoryScopedService
instance belongs to is disposed once a HTTP request returns a response. As the task is not awaited inside the API endpoint method, the ICategoryScopedService
instance is disposed before attempting to output the Datestamp
property to the console application.
The solution is to inject the IServiceScopeFactory
type into the WebApiSharedScopeController
class. Then you create a scope and resolve the ICategoryScopedService
instance from that scope inside the task:
// WebApiSharedScopeController.cs
[ApiController]
[Route("api/[controller]")]
public class WebApiSharedScopeController : ControllerBase
{
private readonly ICategoryScopedService _categoryScopedService;
private readonly IServiceScopeFactory _serviceScopeFactory;
public WebApiSharedScopeController(ICategoryScopedService categoryScopedService,
IServiceScopeFactory serviceScopeFactory
)
{
_categoryScopedService = categoryScopedService;
_serviceScopeFactory = serviceScopeFactory;
}
...
[HttpGet("shared-service-fix")]
public IActionResult SharedServiceFix()
{
Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(5));
// Create a new scope
using var scope = _serviceScopeFactory.CreateScope();
// Resolve the service from that scope
var categoryScopedService = scope.ServiceProvider.GetRequiredService<ICategoryScopedService>();
// Read the datestamp from that scope
Console.WriteLine($"Task.Run Datestamp = {categoryScopedService.Datestamp?.ToString("HH:mm:ss.fffffff")}");
});
Console.WriteLine($"Controller Datestamp = {_categoryScopedService.Datestamp?.ToString("HH:mm:ss.fffffff")}");
return this.Ok();
}
}
When we run the SharedServiceFix
method, the console application outputs two datestamps:
Controller Datestamp = 17:31:17.1594533
Task.Run Datestamp = 17:31:22.1603696
Watch the video
Watch the video where we show you each of these exceptions and how you go about resolving them:
And if you want to try these exceptions out for yourself, you can download the code example.
Related tutorials
