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

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:

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:

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:

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:

Start with small services, inject them into controllers, and with practice, DI will become second nature.