1. Recapitulando o padrão
No capítulo 12 do Módulo 1, você viu Dependency Injection "manual": ClienteService
recebia IClienteRepository pelo construtor, sem criar a implementação
concreta sozinho. O ASP.NET Core formaliza isso com um container de DI embutido:
você registra "quando alguém pedir esta interface, entregue esta implementação" uma única vez, e
o framework resolve a árvore de dependências inteira automaticamente.
2. Registrando serviços
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddScoped<IClienteRepository, ClienteRepository>();
builder.Services.AddScoped<ClienteService>();
builder.Services.AddSingleton<INotificador, NotificadorEmail>();
var app = builder.Build();
...
A partir desse registro, qualquer Controller pode simplesmente pedir essas
dependências no construtor, sem nunca chamar new:
public class ClienteController : Controller
{
private readonly ClienteService _service;
// o framework vê que ClienteController precisa de ClienteService,
// que precisa de IClienteRepository, e monta a cadeia inteira sozinho
public ClienteController(ClienteService service) => _service = service;
}
3. Os três tempos de vida (lifetimes)
| Lifetime | Quando uma nova instância é criada | Uso típico |
|---|---|---|
AddTransient | Toda vez que é solicitada | Serviços leves, sem estado, baratos de criar |
AddScoped | Uma vez por requisição HTTP | A maioria dos serviços — inclusive DbContext e repositórios |
AddSingleton | Uma única vez, para toda a vida da aplicação | Configuração, cache em memória, clientes HTTP reutilizáveis |
Fig. 1 — Quanto mais amplo o lifetime, mais compartilhado (e mais cuidado exige com estado mutável).
Nunca registre um DbContext (ou qualquer coisa que dependa dele)
como Singleton. Como uma única instância é compartilhada entre
todas as requisições simultâneas, isso causa condições de corrida graves —
DbContext não foi projetado para ser usado por múltiplas threads ao
mesmo tempo. AddDbContext<T>(), o método específico do EF Core,
já registra como Scoped por padrão, exatamente por isso.
4. Injeção direto numa Action (menos comum, mas existe)
public async Task<IActionResult> Detalhes(int id, [FromServices] INotificador notificador)
{
// útil quando só uma Action específica precisa de um serviço,
// sem poluir o construtor do Controller inteiro
...
}
5. Onde isso te leva
Com DI formalizado pelo framework, o último capítulo deste módulo cobre o que acontece com uma requisição antes de chegar num Controller e depois de sair dele — o pipeline de middlewares e os filters que interceptam esse fluxo.