Sitemap

Injeção de Dependência em C#: Entendendo Singleton, Scoped e Transient

5 min readSep 2, 2025
Injeção de Dependência em C#: Entendendo Singleton, Scoped e Transient

A injeção de dependência é um assunto recorrente no desenvolvimento de software (saiba mais), principalmente em aplicações bem construídas e que procuram seguir os princípios SOLID, ela promove o princípio da inversão de controle (IoC), permitindo que as dependências de uma classe sejam fornecidas externamente, em vez de serem criadas internamente. Isso facilita o teste unitário, a manutenção e a escalabilidade do código.

No ASP.NET Core, o contêiner de DI integrado gerencia o ciclo de vida das dependências através de três tipos principais: Singleton, Scoped e Transient.

No ASP.NET Core, o contêiner de DI integrado gerencia o ciclo de vida das dependências através de três tipos principais: Singleton, Scoped e Transient. Cada um define como e quando uma instância de um serviço é criada e reutilizada!

Tem sido item recorrente em entrevistas, principalmente de desenvolvedores que alguns se confundam com a definição sobre cada um destes ciclos de vida.

Conceitos Básicos

Antes de mergulharmos nos tipos específicos, é importante entender o fluxo básico da DI no ASP.NET Core:

  1. Registro de Serviços: No método ConfigureServices (ou no Program.cs em .NET 6+), você registra serviços usando o IServiceCollection. (lá nos tempos de Asp.Net Framework, haviam pacotes para gerenciar a injeção de Dependência, como Ninject, Autofac e Castle Windosor).
  2. Resolução de Dependências: O contêiner resolve dependências automaticamente ao injetar instâncias via construtores, métodos ou propriedades.
  3. Ciclo de Vida: O tipo de registro determina o ciclo de vida da instância.

Agora, vamos aos detalhes de cada tipo:

Singleton

Conceito

O modo Singleton cria uma única instância do serviço que é compartilhada por toda a aplicação. Essa instância é criada na primeira vez que é solicitada e reutilizada em todas as solicitações subsequentes. É ideal para serviços que não mantêm estado (stateless) ou que precisam de um estado global compartilhado, como caches ou configurações globais.

Vantagens:

  • Economia de recursos, pois evita criar múltiplas instâncias.
  • Útil para serviços caros de inicializar.

Desvantagens:

  • Pode causar problemas de concorrência se o serviço mantiver estado mutável.
  • Difícil de testar em cenários isolados.

Exemplo

Imagine um serviço de logging global.

// Interface do serviço
public interface ILoggerService
{
void Log(string message);
}

// Implementação
public class LoggerService : ILoggerService
{
private readonly List<string> _logs = new List<string>(); // Estado compartilhado

public void Log(string message)
{
_logs.Add(message);
Console.WriteLine($"Log: {message}. Total logs: {_logs.Count}");
}
}

// Registro no Program.cs ou Startup.cs
builder.Services.AddSingleton<ILoggerService, LoggerService>(); // Registro como Singleton

Você aí implementa numa classe controller a chamada assim:

public class HomeController : Controller
{
private readonly ILoggerService _logger;

public HomeController(ILoggerService logger)
{
_logger = logger;
}

public IActionResult Index()
{
_logger.Log("Acesso à página inicial");
return View();
}
}

Se múltiplos usuários acessarem a página, todos usarão a mesma instância de LoggerService, e o contador de logs será incrementado globalmente.

Scoped

Conceito

O modo Scoped cria uma instância por “escopo”, que em aplicações web ASP.NET Core corresponde a uma solicitação HTTP. Fora do contexto web, você pode criar escopos manualmente com IServiceScopeFactory. É perfeito para serviços que precisam de estado por solicitação, como repositórios de banco de dados que gerenciam transações.

Vantagens:

  • Isola o estado por solicitação, evitando vazamentos de dados entre usuários.
  • Bom equilíbrio entre desempenho e isolamento.

Desvantagens:

  • Não é adequado para estados globais.
  • Em cenários não-web, requer gerenciamento manual de escopos.

Exemplo

Considere um serviço de repositório para um banco de dados.

// Interface
public interface IRepository
{
void AddItem(string item);
List<string> GetItems();
}

// Implementação
public class Repository : IRepository
{
private readonly List<string> _items = new List<string>(); // Estado por escopo

public void AddItem(string item)
{
_items.Add(item);
}

public List<string> GetItems()
{
return _items;
}
}

// Registro
builder.Services.AddScoped<IRepository, Repository>(); // Registro como Scoped

Em uma classe controller:

public class DataController : Controller
{
private readonly IRepository _repo;

public DataController(IRepository repo)
{
_repo = repo;
}

public IActionResult Add(string item)
{
_repo.AddItem(item);
var items = _repo.GetItems(); // Retorna itens apenas desta solicitação
return Json(items);
}
}

Em uma única solicitação HTTP, múltiplas chamadas ao _repo usarão a mesma instância. Em solicitações diferentes, instâncias separadas serão criadas.

Transient

Conceito

O modo Transient cria uma nova instância toda vez que o serviço é solicitado. É o mais “leve” e seguro para serviços que não compartilham estado ou que são baratos de criar.

Vantagens:

  • Máximo isolamento; cada resolução é independente.
  • Evita problemas de concorrência.

Desvantagens:

  • Pode ser ineficiente se o serviço for caro de instanciar.
  • Consome mais memória em cenários de alta carga.

Exemplo

Um serviço simples de cálculo.

// Interface
public interface ICalculator
{
int Add(int a, int b);
}

// Implementação
public class Calculator : ICalculator
{
public Calculator()
{
Console.WriteLine("Nova instância de Calculator criada!");
}

public int Add(int a, int b)
{
return a + b;
}
}

// Registro
builder.Services.AddTransient<ICalculator, Calculator>(); // Registro como Transient

Em uma classe controller:

public class MathController : Controller
{
private readonly ICalculator _calc;

public MathController(ICalculator calc)
{
_calc = calc; // Nova instância aqui
}

public IActionResult Sum(int a, int b)
{
var result = _calc.Add(a, b); // Se injetar outra dependência que use ICalculator, será outra instância
return Json(result);
}
}

Cada injeção ou resolução manual cria uma nova instância, como mostrado no construtor.

Comparação Rápida

Press enter or click to view image in full size
tabela comparativa singleton, scoped e transient

Conclusão

Escolher o ciclo de vida correto é crucial para evitar bugs sutis, como vazamentos de memória ou estados compartilhados indesejados. Sempre teste seus serviços em cenários reais e considere o contexto da aplicação (web vs. console). Para mais avançado, explore capturas de dependências (e.g., Singleton capturando Scoped) e use ferramentas como o Visual Studio para depurar o contêiner de DI.

Gostou do artigo? clique no ícone👏e me siga para ver as próximas publicações !! Quer ver mais conteúdos, acesse minhas redes através do Linktree: https://linktree.com/nizzola

Outros Posts sobre Injeção de Dependência

Simplificando a injeção de dependência para serviços e repositórios

Problemas com Worker Services e Injeção de dependência no .NET?

Como fazer injeção de dependência em um console application com .Net Core.

Afinal o que é injeção de dependência?

--

--

Marcio Nizzola
Marcio Nizzola

Written by Marcio Nizzola

Microsoft MVP | Master Software Architect na CI&T | Prof. da Etec Itu | Membro Fundador da Comunidade Itu Developers.

No responses yet