13. Dependency injection and options
Sections
Reading settings
Natural voice recorded for this course, the same in every browser.
No voice for this language in this browser.
More voices
Code and English terms
Off: the page’s voice reads everything, with no pause between two voices.
No voice for this language in this browser.
More voices
More voices: Microsoft Edge has natural online voices, and Windows (Settings › Time & language › Speech), macOS and Android install others.
ASP.NET Core dependency injection is an ownership system. A lifetime says how long one instance and its state may be shared.
Lifetimes are not performance hints
Section titled “Lifetimes are not performance hints”- singleton: one instance for the root provider; it must be thread-safe and cannot capture request-scoped state;
- scoped: one instance per explicit scope, normally one HTTP request;
- transient: a new instance per resolution; disposable transients are still owned by their resolving container.
The executable lesson proves identity across resolutions and enables scope validation. A singleton constructor that requests a scoped service is rejected:
same scope returns same instance: Truesingleton returns same instance: Truedifferent scopes return different instances: Truecaptive scoped dependency rejected: TrueKeyed services select an implementation by an explicit key. Use them when the key is part of composition policy, not as a service locator spread through domain code.
Options are a configuration contract
Section titled “Options are a configuration contract”IOptions<T> is a singleton view, IOptionsSnapshot<T> recomputes per scope, and IOptionsMonitor<T> observes changes. Reloadability is not automatically safe: decide whether an in-flight operation sees the old or new value and whether the setting can change without rebuilding dependent state.
The lesson configures an invalid capacity and proves OptionsValidationException is raised. In hosted applications, ValidateOnStart moves that failure to startup rather than the first request.
Run the proof
Section titled “Run the proof”dotnet run --project code/csharp-advanced/Advanced -c Release -- l13The checked transcript is expected/l13.txt.
Exercises
Section titled “Exercises”- Inject a scoped repository into a singleton cache and enable
ValidateScopes. Repair the ownership without resolving from the root provider. - Model two keyed formatters, then move key selection to the composition root.
- Reload a capacity option while work is in flight and write the invariant that must remain true.
Solutions
- Make the consumer scoped, pass immutable data into the singleton, or inject
IServiceScopeFactoryonly into an infrastructure coordinator that creates and disposes each scope explicitly. - Resolve the keyed formatter when wiring the use case and inject the unkeyed contract into domain code. The domain should not know DI keys.
- Existing queues cannot silently change capacity. Apply the new value to newly created queues, or perform an explicit migration with admission paused and observable completion.