Quando começamos a trabalhar com ASP.NET Core, uma das primeiras coisas que aprendemos é a Injeção de Dependência (Dependency Injection).
Mas existe uma dúvida que acompanha muitos desenvolvedores por anos:
Quando devo usar Singleton, Scoped ou Transient?
Se você já ficou em dúvida sobre qual Lifetime utilizar ou já recebeu erros estranhos envolvendo DbContext, este artigo é para você.
Vamos entender esses conceitos de uma forma simples e prática.
Antes de tudo: o que é um Lifetime?
Imagine que sua aplicação é um hotel.
Os serviços registrados no container de Injeção de Dependência são hóspedes.
O Lifetime define:
Quanto tempo esse hóspede ficará hospedado antes de ir embora.
Dependendo da configuração escolhida, um objeto poderá:
- Existir durante toda a aplicação
- Existir apenas durante uma requisição
- Ser recriado sempre que solicitado
É exatamente isso que Singleton, Scoped e Transient fazem.
Singleton
🏨 Analogia: O gerente do hotel
Imagine o gerente do hotel.
Existe apenas um.
Não importa quantos hóspedes cheguem ou quantos funcionários precisem de ajuda.
Todos falam com a mesma pessoa.
Request 1
\
Request 2 ---> Gerente Único
/
Request 3
O que acontece na aplicação?
O ASP.NET Core cria apenas uma instância e a reutiliza durante toda a vida da aplicação.
builder.Services.AddSingleton<EmailService>();
Request 1 -> Instância A
Request 2 -> Instância A
Request 3 -> Instância A
Sempre a mesma instância.
Quando usar?
✅ Cache
✅ Configurações
✅ Serviços sem estado
✅ HttpClientFactory
✅ Clients reutilizáveis
⚠️ Cuidado com estado compartilhado
Imagine que você registre o seguinte serviço:
public class UserContext
{
public string Username { get; set; }
}
E registre como Singleton:
builder.Services.AddSingleton<UserContext>();
Agora todos os usuários da aplicação compartilharão a mesma instância.
Isso significa que informações de um usuário podem sobrescrever informações de outro.
Por esse motivo, Singleton deve ser utilizado apenas quando o serviço não possui dados específicos de usuários.
Scoped
🍽️ Analogia: O garçom do restaurante
Agora imagine os garçons.
Cada mesa recebe um garçom específico.
Durante todo o atendimento, aquela mesa conversa com o mesmo garçom.
Quando a refeição termina, o atendimento acaba.
Mesa 1 -> Garçom A
Mesa 2 -> Garçom B
Mesa 3 -> Garçom C
O que acontece na aplicação?
Uma nova instância é criada para cada requisição HTTP.
builder.Services.AddScoped<UserService>();
Request 1 -> Instância A
Request 2 -> Instância B
Request 3 -> Instância C
Quando usar?
Na prática, esse é o Lifetime mais utilizado em aplicações ASP.NET Core.
✅ DbContext
✅ Repositories
✅ Services de negócio
✅ Unit Of Work
Exemplo real
O próprio Entity Framework registra o DbContext como Scoped.
builder.Services.AddDbContext<AppDbContext>();
Cada requisição recebe seu próprio contexto.
Isso evita conflitos entre usuários.
💡 Por que o DbContext é Scoped?
Imagine duas requisições simultâneas:
Request A
Request B
Se ambas compartilhassem o mesmo DbContext, poderiam ocorrer conflitos de rastreamento de entidades, concorrência e inconsistências nos dados.
Ao utilizar Scoped, cada requisição recebe sua própria instância.
Por isso o Entity Framework registra o DbContext dessa forma por padrão.
Transient
🥤 Analogia: Um copo descartável
Imagine um copo descartável.
Você pega.
Usa.
Descarta.
Precisa de outro?
Recebe um novo.
Sempre.
Uso 1 -> Copo A
Uso 2 -> Copo B
Uso 3 -> Copo C
O que acontece na aplicação?
Uma nova instância é criada toda vez que o serviço é solicitado.
builder.Services.AddTransient<LogFormatter>();
Mesmo dentro da mesma requisição:
Resolução 1 -> Instância A
Resolução 2 -> Instância B
Resolução 3 -> Instância C
Quando usar?
✅ Formatadores
✅ Conversores
✅ Geradores de PDF
✅ Geradores de Token
✅ Serviços extremamente leves
Comparativo rápido
| Característica | Singleton | Scoped | Transient |
|---|---|---|---|
| Uma instância para toda aplicação | ✅ | ❌ | ❌ |
| Uma instância por request | ❌ | ✅ | ❌ |
| Nova instância sempre | ❌ | ❌ | ✅ |
| Compartilha estado | ✅ | Apenas na request | ❌ |
| Mais utilizado | ❌ | ✅ | ❌ |
Aproximadamente 80% dos serviços em aplicações ASP.NET Core acabam sendo registrados como Scoped.
O erro que quase todo desenvolvedor já viu
Registrar um Singleton que depende de um Scoped.
builder.Services.AddSingleton<UserService>();
builder.Services.AddScoped<AppDbContext>();
public class UserService
{
public UserService(AppDbContext context)
{
}
}
Resultado:
Cannot consume scoped service
from singleton
O motivo é simples:
- O Singleton vive durante toda a aplicação.
- O Scoped vive apenas durante a requisição.
O container não consegue manter uma referência válida para um objeto que será destruído antes dele.
Resumo Visual
Conclusão
Singleton, Scoped e Transient parecem apenas detalhes da Injeção de Dependência.
Mas eles impactam diretamente:
- Performance
- Consumo de memória
- Escalabilidade
- Concorrência
- Segurança dos dados
Entender os Lifetimes da Injeção de Dependência vai muito além de decorar três palavras.
Eles definem como os objetos da sua aplicação vivem, são compartilhados e são descartados.
Se você estiver em dúvida:
Singleton -> Uma instância para toda aplicação
Scoped -> Uma instância por requisição
Transient -> Uma nova instância sempre
Na maioria dos projetos ASP.NET Core:
DbContext -> Scoped
Repositories -> Scoped
Services -> Scoped
Cache -> Singleton
Configurações -> Singleton
Formatadores -> Transient
Conversores -> Transient
Geradores -> Transient
Dominar esses conceitos é um dos primeiros passos para construir aplicações robustas, escaláveis e fáceis de manter.
💬 E você?
Qual Lifetime você mais utiliza nos seus projetos hoje?

Top comments (0)