ASP.NET Core includes a built-in DI container. Register services in Program.cs; the framework injects them into controllers, Razor Pages, and middleware via constructors.
Registration lifetimes
- Transient — new instance every time requested
- Scoped — one per HTTP request (typical for DbContext)
- Singleton — one for app lifetime (caches, config readers)
Example
builder.Services.AddScoped<IEmailSender, SmtpEmailSender>();
public class AccountController : Controller {
private readonly IEmailSender _email;
public AccountController(IEmailSender email) => _email = email;
}
Important interview questions and answers
- Q: Why Scoped for DbContext?
A: One context per request aligns with unit of work; sharing across requests causes threading and stale data bugs. - Q: Captive dependency?
A: Singleton holding Scoped service—lifetime mismatch; fix by adjusting registrations or using factories.
Self-check
- Which lifetime fits IEmailSender per request?
- Why inject interfaces instead of concrete classes?
Pitfall: Registering DbContext as Singleton causes threading bugs—always Scoped. Never inject Scoped services into Singleton without a factory.
Interview prep
- Why Scoped for DbContext?
One DbContext per HTTP request aligns with unit of work—sharing across requests causes threading issues and stale tracked entities.