skipnothing/.NET Platform
UNIT 03

Dependency Injection

The pattern that wires everything together

DI isn't just a pattern in .NET, it's the architecture. Every framework feature plugs into it.

Loading…
TOPICS
#12

Why Dependency Injection

Trace one hard-coded `new` out of a constructor and watch it unlock testing, swapping, and the dependency inversion principle, then meet the assembler .NET builds in.

A constructor that calls `new SmtpEmailSender()` has quietly chosen, for every caller and every test, the one and only way this class will ever send mail. Delete that single line and the class stops deciding.

12 min
#13

The .NET DI Container

Register services into an IServiceCollection, build it into a provider, and watch one GetRequiredService call walk the constructor graph and assemble the whole object tree, failing loud when a registration is missing.

Wiring `new OrderService(new SmtpEmailSender(new SmtpConfig(...)))` by hand at every call site is the chore .NET will take over. Write down which concrete class fills which request, ask for the top of the graph, and the whole wired object comes back.

14 min
#14

Service Lifetimes

Pick Transient, Scoped, or Singleton from how a service holds state, predict how many instances exist across requests, and catch the captive dependency that scope validation only flags in Development.

You change `AddScoped` to `AddSingleton` to stop rebuilding the same object, the app still runs, and three requests later one user sees another user's data. One word on a registration decided that.

15 min
#15

Registration Patterns

Hand the container a factory delegate, cover every closed type with one open generic line, and reach for TryAdd and TryAddEnumerable so a library's default yields to the app, each just a different shape of descriptor row.

Every service in your app entered through one line: `services.AddScoped<IOrderRepository, OrderRepository>()`. That line has four other shapes, and picking the wrong one is how a library's default silently wins over yours, or never does.

12 min
#16

Keyed Services

Tag each registration of one interface with a key, then resolve the one you mean with FromKeyedServices or GetRequiredKeyedService, and watch keyed and keyless rows ignore each other's lookups.

Register `EmailMessageWriter` and `SmsMessageWriter` both as `IMessageWriter`, then ask for one, and the container hands back whichever you registered last. There was no built-in way to say which one you meant, until one extra field on the row.

10 min
#17

DI Anti-Patterns

Spot four DI traps that compile and run: a constructor hiding its real dependency behind IServiceProvider, a singleton freezing a per-request service, a parameter list outgrowing its class, and a BuildServiceProvider call minting a second set of singletons, then fix each.

A class takes one constructor parameter, `IServiceProvider`, and compiles without complaint. Nothing in its signature admits it needs an email sender, so when that registration goes missing the failure lands on a customer's order, not your build.

14 min
UNLOCKS
All units