Dependency Injection

Back

Loading concept...

🎯 Dependency Injection in ASP.NET Core

The Magic Coffee Shop Analogy β˜•

Imagine you run a coffee shop. Every morning, your barista needs:

  • Coffee beans
  • Milk
  • A coffee machine

Now, there are two ways to get these supplies:

The OLD Way (No DI): The barista goes to the store, picks beans, finds milk, buys a machine… EVERY. SINGLE. DAY. 😰 Exhausting! What if the store changes location? Everything breaks!

The NEW Way (With DI): Someone delivers everything to the barista each morning. The barista just says β€œI need coffee beans” and poof β€” they appear! 😎 Easy! If the supplier changes, the barista doesn’t even notice.

That’s Dependency Injection! Your code says β€œI need this thing” and ASP.NET Core delivers it automatically.


🌟 What is ASP.NET Core DI?

Dependency Injection (DI) is a built-in superpower in ASP.NET Core.

Think of it like a magical delivery service that:

  1. Knows what your classes need
  2. Creates those things
  3. Delivers them automatically

Why Should You Care?

Without DI 😫 With DI 😊
Classes create their own dependencies Dependencies are delivered
Hard to test Easy to test
Tightly coupled code Loosely coupled code
Change one thing, break everything Change is painless

πŸ“¦ The DI Container

ASP.NET Core has a container β€” think of it as a smart warehouse.

graph TD A["Your Code"] -->|Asks for| B["DI Container"] B -->|Delivers| C["Ready-to-use Service"] B -->|Stores| D["Service Recipes"]

The container:

  • Stores recipes for creating services
  • Builds services when asked
  • Manages their lifetime (more on this soon!)

🏭 Service Registration

Before the magic works, you must tell the container what services exist.

This happens in Program.cs:

// "Hey container, when someone
// asks for ICoffeeService,
// give them CoffeeService!"

builder.Services.AddTransient
    <ICoffeeService, CoffeeService>();

The Three Key Methods

Method When to Use
AddTransient New instance every time
AddScoped One per request
AddSingleton One for the whole app

Real Example

// Program.cs

var builder = WebApplication
    .CreateBuilder(args);

// Register services
builder.Services.AddTransient
    <IEmailSender, SmtpEmailSender>();

builder.Services.AddScoped
    <IShoppingCart, ShoppingCart>();

builder.Services.AddSingleton
    <ILogger, FileLogger>();

var app = builder.Build();

⏰ Service Lifetimes

This is crucial! Each service lives for a different duration.

πŸ”„ Transient β€” The Disposable Cup

graph TD R1["Request 1"] --> T1["New Instance"] R1 --> T2["New Instance"] R2["Request 2"] --> T3["New Instance"]

Every time someone asks = brand new instance.

builder.Services.AddTransient
    <INotification, EmailNotification>();

Use for: Lightweight, stateless services.


πŸ“‹ Scoped β€” The Refillable Cup

graph TD R1["Request 1"] --> S1["Same Instance"] R1 --> S1 R2["Request 2"] --> S2["Different Instance"]

Same instance throughout ONE request. New instance for the next request.

builder.Services.AddScoped
    <IShoppingCart, ShoppingCart>();

Use for: Database contexts, per-request data.


πŸ† Singleton β€” The Permanent Mug

graph TD R1["Request 1"] --> S["Same Instance"] R2["Request 2"] --> S R3["Request 100"] --> S

ONE instance for the entire application lifetime.

builder.Services.AddSingleton
    <ICache, MemoryCache>();

Use for: Caching, configuration, logging.


⚠️ Lifetime Mismatch Warning!

NEVER inject a Scoped service into a Singleton!

// ❌ WRONG - This will break!
public class MySingleton
{
    // Scoped inside Singleton = BAD
    private readonly IScopedService _scoped;
}

Why? The Singleton lives forever, but the Scoped service should die after each request. Chaos ensues!


🎁 Service Resolution

Resolution = Getting a service from the container.

Constructor Injection (Most Common)

Your class declares what it needs in its constructor:

public class OrderController
{
    private readonly IOrderService _orders;
    private readonly IEmailSender _email;

    // "I need these two things!"
    public OrderController(
        IOrderService orders,
        IEmailSender email)
    {
        _orders = orders;
        _email = email;
    }
}

ASP.NET Core automatically provides them!


Method Injection

Sometimes you need a service in just one method:

public IActionResult SendReport(
    [FromServices] IReportGenerator gen)
{
    // gen is injected just for this method
    return Ok(gen.CreateReport());
}

Manual Resolution (Rarely Needed)

// Inside a controller or middleware
var service = HttpContext
    .RequestServices
    .GetService<IMyService>();

// Or require it (throws if missing)
var required = HttpContext
    .RequestServices
    .GetRequiredService<IMyService>();

πŸ”‘ Keyed Services (.NET 8+)

What if you have multiple implementations of the same interface?

The Coffee Shop Problem: You have TWO coffee machines β€” one for espresso, one for drip. Both are ICoffeeMachine. How do you choose?

Keyed Services to the rescue!

Registration with Keys

builder.Services.AddKeyedSingleton
    <ICoffeeMachine, EspressoMachine>
    ("espresso");

builder.Services.AddKeyedSingleton
    <ICoffeeMachine, DripMachine>
    ("drip");

Resolution with Keys

public class CoffeeController
{
    private readonly ICoffeeMachine _espresso;

    public CoffeeController(
        [FromKeyedServices("espresso")]
        ICoffeeMachine espresso)
    {
        _espresso = espresso;
    }
}

Another Way to Resolve

var espresso = provider
    .GetKeyedService<ICoffeeMachine>
    ("espresso");

var drip = provider
    .GetKeyedService<ICoffeeMachine>
    ("drip");

🎯 Quick Reference

graph LR A["Service Registration"] -->|AddTransient| B["New Every Time"] A -->|AddScoped| C["One Per Request"] A -->|AddSingleton| D["One Forever"] A -->|AddKeyed*| E["Multiple by Key"] F["Service Resolution"] -->|Constructor| G["Auto-injected"] F -->|FromServices| H["Method Parameter"] F -->|GetService| I["Manual Lookup"]

πŸš€ Complete Example

// Program.cs
var builder = WebApplication
    .CreateBuilder(args);

// 1. Register services
builder.Services.AddTransient
    <IEmailSender, SmtpEmailSender>();

builder.Services.AddScoped
    <IUserRepository, SqlUserRepository>();

builder.Services.AddSingleton
    <ICache, RedisCache>();

builder.Services.AddKeyedSingleton
    <IPayment, StripePayment>("stripe");

builder.Services.AddKeyedSingleton
    <IPayment, PayPalPayment>("paypal");

var app = builder.Build();

// 2. Use them!
app.MapGet("/", (
    IEmailSender email,
    IUserRepository users,
    ICache cache,
    [FromKeyedServices("stripe")]
    IPayment payment) =>
{
    return "Services injected! ✨";
});

app.Run();

πŸŽ‰ You Did It!

You now understand:

βœ… DI Overview β€” The magical delivery service βœ… Service Lifetimes β€” Transient, Scoped, Singleton βœ… Registration β€” Teaching the container βœ… Resolution β€” Getting what you need βœ… Keyed Services β€” Choosing between multiple options

Remember the coffee shop:

  • Register = Tell the supplier what you need
  • Lifetime = How long supplies last
  • Resolution = Getting your delivery
  • Keyed = Picking which supplier

Now go build something amazing! πŸš€

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.