Introduction
When you are building applications in ASP.NET Core, you will quickly encounter the term Dependency Injection (DI).
But what exactly is dependency injection, and why is it so important?
In simple words, Dependency Injection is a technique to give an object the things it needs (its dependencies) instead of the object creating them itself.
This may sound technical, but it helps in making your code cleaner, more modular, and easier to test.
This article explains dependency injection in ASP.NET Core from scratch, with simple examples and easy-to-understand explanations for beginners.
What is Dependency Injection?
Every object in an application often depends on other objects to work properly. These are called dependencies.
For example:
public class UserService
{
private readonly UserRepository _userRepository = new UserRepository();
public IEnumerable<User> GetUsers()
{
return _userRepository.GetAllUsers();
}
}
In this code, UserService creates its own UserRepository.
Problem
If UserRepository changes, you must update UserService.
Testing UserService becomes harder because it always uses a real UserRepository.
Solution: Instead of creating UserRepository inside UserService, we inject it from outside.
public class UserService
{
private readonly IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public IEnumerable<User> GetUsers()
{
return _userRepository.GetAllUsers();
}
}
Here, UserRepository is injected into UserService.
This is Dependency Injection.
Why Use Dependency Injection?
Loose Coupling: Classes don’t create their dependencies, so they are less dependent on specific implementations.
Easier Testing: You can inject fake or mock dependencies during unit tests.
Better Maintainability: Change the dependency in one place, and it affects all classes that use it.
Centralized Configuration: Manage all dependencies in a central place (Startup or Program class).
How Dependency Injection Works in ASP.NET Core
ASP.NET Core has built-in support for dependency injection. It uses a Service Container (also called IoC container) to manage dependencies.
Step 1: Register Services
You register classes and interfaces in the Program.cs file.
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IUserService, UserService>();
AddScoped – One instance per HTTP request.
AddSingleton – Single instance for the entire application lifetime.
AddTransient – New instance every time it is requested.
Step 2: Inject Dependencies into Controllers
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
public UsersController(IUserService userService)
{
_userService = userService;
}
[HttpGet]
public IEnumerable<User> GetUsers()
{
return _userService.GetUsers();
}
}
Types of Dependency Injection in ASP.NET Core
Constructor Injection: Most common. Dependencies are passed via constructor.
Property Injection: Dependencies are set via properties (less common).
Method Injection: Dependencies are passed directly to methods (rarely used).
Example of Constructor Injection (Recommended)
public class OrderService
{
private readonly IOrderRepository _orderRepository;
public OrderService(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public IEnumerable<Order> GetOrders()
{
return _orderRepository.GetAllOrders();
}
}
Example: Dependency Injection with ASP.NET Core
Let’s create a small example with Users:
1. Create Model
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
2. Create Repository Interface
public interface IUserRepository
{
IEnumerable<User> GetAllUsers();
}
3. Create Repository Implementation
public class UserRepository : IUserRepository
{
public IEnumerable<User> GetAllUsers()
{
return new List<User>
{
new User { Id = 1, Name = "Rahul" },
new User { Id = 2, Name = "Priya" }
};
}
}
4. Create Service
public interface IUserService
{
IEnumerable<User> GetUsers();
}
public class UserService : IUserService
{
private readonly IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public IEnumerable<User> GetUsers()
{
return _userRepository.GetAllUsers();
}
}
5. Register Services in Program.cs
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IUserService, UserService>();
6. Inject Service in Controller
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
public UsersController(IUserService userService)
{
_userService = userService;
}
[HttpGet]
public IEnumerable<User> GetUsers()
{
return _userService.GetUsers();
}
}
How It Works
Controller requests IUserService.
ASP.NET Core looks in the Service Container and finds UserService.
UserService needs IUserRepository. The container provides UserRepository.
Everything is automatically injected, and the controller works without manually creating objects.
Lifetime of Services
Transient: New instance every time. AddTransient<TInterface, TImplementation>()
Scoped: One instance per HTTP request. AddScoped<TInterface, TImplementation>()
Singleton: Single instance for the whole application. AddSingleton<TInterface, TImplementation>()
Example
builder.Services.AddTransient<ITransientService, TransientService>();
builder.Services.AddScoped<IScopedService, ScopedService>();
builder.Services.AddSingleton<ISingletonService, SingletonService>();
Benefits of DI in ASP.NET Core
Cleaner and more organized code.
Reduced coupling between classes.
Easier unit testing using mocks.
Centralized configuration of services.
Makes code flexible and scalable for future changes.
Common Mistakes to Avoid
Not using interfaces: Avoid injecting concrete classes directly.
Mixing lifetimes incorrectly: e.g., injecting a transient service into a singleton.
Creating dependencies manually in controllers: defeats the purpose of DI.
Registering services multiple times unnecessarily: may lead to unexpected behavior.
Conclusion
Dependency Injection is a powerful feature in ASP.NET Core that helps you write clean, testable, and maintainable applications.
By using DI
You no longer manually create objects in controllers.
Dependencies are managed automatically by the framework.
You can easily swap implementations for testing or future updates.
For beginners, mastering DI is essential before moving to advanced topics like Repository Pattern, Unit of Work, and Middleware in ASP.NET Core.