Lesson 9 — Beginner

Dependency Injection Explained Simply

.NETBeginnerC#Tutorial

Imagine you run a pizza shop. The chef does not grow tomatoes, raise cows for cheese, or forge ovens. Suppliers deliver ingredients; the chef focuses on cooking. If tomato quality changes, you swap suppliers — the chef's recipe stays the same.

Dependency injection (DI) applies that idea to code. A class should not secretly build every helper it needs. It should receive them — injected — from the outside. ASP.NET Core does this automatically, which is why your API from Lesson 7 can cleanly use a database from Lesson 8.

What Is Dependency Injection?

A dependency is something a class needs to do its job — a database context, an email sender, a payment gateway.

Dependency injection means passing those needs in instead of creating them with new inside the class.

Without DI, an order service might look like:

// Tight coupling — hard to test or swap implementations
var db = new AppDbContext();
var email = new SmtpEmailService();

With DI, the framework supplies ready-made instances configured once at startup.

Constructor Injection

The most common pattern — list dependencies in the constructor:

public class OrderService
{
    private readonly AppDbContext _db;

    public OrderService(AppDbContext db)
    {
        _db = db;
    }

    public double GetCustomerTotal(int customerId)
    {
        return _db.Orders
            .Where(o => o.CustomerId == customerId)
            .Sum(o => o.Amount);
    }
}

OrderService declares "I need a database." ASP.NET Core's DI container (built into the framework) creates OrderService and passes a configured AppDbContext automatically when someone asks for an order calculation.

Registering Services

In Program.cs, you tell the container how to build each type:

builder.Services.AddDbContext<AppDbContext>();
builder.Services.AddScoped<OrderService>();

Service lifetimes control how long instances live:

  • Transient — new instance every time (lightweight helpers)
  • Scoped — one per HTTP request (typical for DbContext)
  • Singleton — one for entire app (configuration, caches)

DI in a Request

HTTP request arrives
      ↓
Container creates scoped DbContext
      ↓
Injects DbContext into OrderService
      ↓
Injects OrderService into API endpoint
      ↓
Request ends → scoped objects disposed

Like a food delivery platform assigning one driver and one insulated bag per order — resources match the job duration, then get recycled.

Using DI in an Endpoint

app.MapGet("/api/customer/{id}/total", (int id, OrderService orders) =>
{
    double total = orders.GetCustomerTotal(id);
    return new { CustomerId = id, TotalSpent = total };
});

Notice OrderService orders as a parameter — not created by you. The container sees the type and injects it. Same magic works for controllers in larger apps.

Real-World Example

A banking app might inject:

  • AppDbContext for account data
  • IFraudChecker for suspicious transaction rules
  • ISmsGateway for OTP texts

In testing, developers swap real SMS with a fake that logs messages instead of sending them — because DI depends on interfaces, not concrete phone networks. That flexibility saves money and prevents accidental texts to real customers during development.

Common Misconceptions

"DI is just a buzzword for passing parameters." True at the micro level — but the container manages lifetimes, graphs of nested dependencies, and configuration centrally.

"Singleton is always best — one instance is efficient." DbContext must not be singleton — it is not thread-safe for concurrent requests. Use scoped.

"DI means I never use new." You still new plain data objects (new BankAccount()). Do not new infrastructure services inside business logic.

"DI is optional in ASP.NET Core." The framework is built around it. Fighting DI makes life harder, not simpler.

Quick Recap

  • Dependencies are services a class needs.
  • Injection supplies them from outside — usually via constructor.
  • Register services in Program.cs with appropriate lifetimes.
  • Endpoints and controllers receive services as parameters.
  • DI improves testing and swapping implementations (real SMS vs fake).

Summary

Dependency injection is organised sharing — the pizza chef focusing on cooking while trusted suppliers handle ingredients. Your API endpoints focus on HTTP while the container handles wiring databases and business services.

Lesson 10 closes the path with debugging — finding why injected services or EF queries misbehave when something goes wrong.

Frequently Asked Questions

A pattern where a class receives the objects it needs from an external provider instead of constructing them internally with new.

Built into ASP.NET Core — it stores service registrations and automatically creates objects with dependencies satisfied.

Listing dependencies as constructor parameters — the recommended way to receive services in .NET classes.

One instance per HTTP request — ideal for DbContext so each request gets a fresh database session.

Manual construction couples classes tightly, duplicates configuration, and makes unit testing difficult because you cannot easily substitute fakes.

ASP.NET Core uses DI from the smallest Web API template upward — you will use it immediately, not only in enterprise projects.

Key Takeaways

  • DI passes dependencies in rather than building them inside classes.
  • Register services once in Program.cs.
  • Choose lifetimes carefully: Scoped for DbContext, Singleton for config.
  • Constructor injection is the standard pattern in .NET.
  • DI enables testing with fake email, payment, and database services.

Suggested Next Reads

Share: LinkedIn Facebook X

Need help implementing this in your organization?

Contact Emerrank Consultancy