Módulo 3 · Arquitetura MVC — Capítulo 06

Injeção de Dependência no ASP.NET Core

Como o padrão de Dependency Injection do Módulo 1 vira parte do próprio framework — e os três "tempos de vida" que decidem quando uma instância é criada e descartada.

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

C# Program.cs
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:

C# o container resolve tudo sozinho
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)

LifetimeQuando uma nova instância é criadaUso típico
AddTransientToda vez que é solicitadaServiços leves, sem estado, baratos de criar
AddScopedUma vez por requisição HTTPA maioria dos serviços — inclusive DbContext e repositórios
AddSingletonUma única vez, para toda a vida da aplicaçãoConfiguração, cache em memória, clientes HTTP reutilizáveis
Transient nova instância cada pedido Scoped mesma instância durante 1 requisição nova a cada requisição nova Singleton uma única instância para toda a aplicação, do início ao fim

Fig. 1 — Quanto mais amplo o lifetime, mais compartilhado (e mais cuidado exige com estado mutável).

Atenção — o erro mais comum

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)

C# [FromServices]
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.

📌 Resumo do capítulo

  • O container de DI do ASP.NET Core formaliza o padrão Dependency Injection do Módulo 1 — registre uma vez, o framework resolve a árvore de dependências.
  • AddTransient: nova instância sempre; AddScoped: uma por requisição; AddSingleton: uma para toda a aplicação.
  • Nunca registre DbContext (ou dependentes dele) como Singleton — AddDbContext já usa Scoped corretamente por padrão.

✏️ Praticando

  1. Registre IClienteRepository/ClienteRepository e ClienteService no Program.cs, com o lifetime apropriado para cada um.
  2. Explique, em suas próprias palavras, por que um serviço que guarda estado mutável específico de um usuário não deveria ser Singleton.