Dependency Injection (DI) is one of the most important concepts in ASP.NET Core.
Almost every project—small or enterprise—uses DI because it makes your code cleaner, more testable, and easier to maintain.
In this article, we will explore DI in simple language, with real examples that new developers can understand.
What Is Dependency Injection?
To understand DI, let’s look at what a “dependency” is.
A dependency is simply another class that your class needs to work.
Example
A controller needs a service.
public class OrderController
{
private readonly OrderService _orderService;
public OrderController()
{
_orderService = new OrderService(); // ← dependency created manually
}
}
This approach has a big problem: You are hard-coding the dependency using new. Problems Without DI
Your classes become tightly coupled
Hard to replace or mock services
Unit testing becomes painful
Changing one class forces you to modify others
This is why ASP.NET Core uses Dependency Injection by default.
What DI Does?
With DI, you don’t create the dependency yourself.
You ask ASP.NET Core to provide it.
ASP.NET Core automatically creates:
Services
Repositories
Loggers
Database contexts
Configurations
And injects them where they are needed.
⭐ Basic Example of DI
Step 1: Create an Interface
public interface IOrderService
{
string GetOrderStatus(int id);
}
Step 2: Create the Service
public class OrderService : IOrderService
{
public string GetOrderStatus(int id)
{
return $"Order {id} is completed.";
}
}
Step 3: Register Service in Program.cs
builder.Services.AddScoped<IOrderService, OrderService>();
Step 4: Inject Into Controller
[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
private readonly IOrderService _orderService;
public OrderController(IOrderService orderService)
{
_orderService = orderService;
}
[HttpGet("{id}")]
public IActionResult Get(int id)
{
return Ok(_orderService.GetOrderStatus(id));
}
}
✔️ No new
✔️ Clean
✔️ Testable
✔️ Follows best practices
DI Lifetimes (Very Important)
ASP.NET Core has 3 main lifetimes:
1. Transient – (AddTransient)
Created every time it is requested.
builder.Services.AddTransient<IEmailService, EmailService>();
Used for:
2. Scoped – (AddScoped)
Created once per HTTP request.
builder.Services.AddScoped<IOrderService, OrderService>();
Used for:
3. Singleton – (AddSingleton)
Created only once and reused for whole application lifetime.
builder.Services.AddSingleton<ILogService, LogService>();
Used for:
Caching
Logging
Configurations
Common Mistake: Using 'new' inside Controllers
Beginners often do this:
var service = new OrderService();
This breaks DI completely.
Instead, always inject dependencies through the constructor:
public OrderController(IOrderService service)
{
_orderService = service;
}
Best Practices for DI
Here are some professional-level tips:
Use interfaces for all services
Avoid injecting more than 3–4 services into one class
Prefer Scoped for database-related classes
Don’t put business logic inside controllers
Keep your services small and focused
Conclusion
Dependency Injection is not just a feature of ASP.NET Core — it’s the foundation of how modern .NET applications are built.
If you want to grow as a professional C# developer, mastering DI will make your code:
Cleaner
Easier to test
Easier to maintain
More structured
More scalable
Start with small services, inject them into controllers, and with practice, DI will become second nature.