Dependency Injection for Dummies
When I first started learning .NET, one concept that confused me a lot was Dependency Injection. Mostly because of the way it was explained often with a lot of boring fancy talk.
So in this article I will explain it in very simple terms.
What is Dependency Injection?
Dependency Injection is a technique where a class does not create its own dependencies.
Instead, the dependencies are provided to the class from through the constructor.
This reduces tight coupling and makes the application easier to maintain and test.
Example
Without Dependency Injection:
public class UserService
{
private UserRepository _userRepository = new UserRepository();
}
The service creates its own dependency, which makes it tightly coupled.
With Dependency Injection:
public class UserService
{
private readonly UserRepository _userRepository;
public UserService(UserRepository userRepository)
{
_userRepository = userRepository;
}
}
Now the dependency is injected through the constructor.
But where does UserRepository come from?
Registering Services
In ASP.NET Core we register services in Program.cs.
builder.Services.AddScoped<UserRepository>();
ASP.NET Core has a built-in IoC container (Inversion of Control container) that manages these dependencies.
When the application needs UserRepository, the container automatically provides it.
Service Lifetimes
ASP.NET Core supports three lifetimes for services.
Scoped
A Scoped service is created once per HTTP request.
This is commonly used for things like database contexts.
Singleton
A Singleton service is created once for the entire application lifetime.
Every request receives the same instance.
Transient
A Transient service creates a new instance every time it is requested.
This is useful for lightweight services that don't need to maintain state.
Dependency Inversion
While learning Dependency Injection, you will also hear about the Dependency Inversion Principle, which is part of the SOLID principles.
It means:
High-level modules should not depend on low-level modules. Both should depend on abstractions.
In practice, this usually means using interfaces.
Example:
public interface IUserRepository
{
User GetById(int id);
}
Implementation:
public class UserRepository : IUserRepository
{
public User GetById(int id)
{
// database logic
}
}
Service registration:
builder.Services.AddScoped<IUserRepository, UserRepository>();
Now your classes depend on interfaces instead of concrete implementations.
Final Thoughts
Dependency Injection might sound complicated at first, but the core idea is simple:
- Don't create dependencies inside your classes
- Receive them from the outside
- Let the framework manage their lifecycle
Once you understand this, many parts of ASP.NET Core architecture start to make much more sense.