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.
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.
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.
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.
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.
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.