Container de Injeção de Dependência para C# (.NET): guia completo
Injeção de Dependência (DI) é um dos princípios-chave do desenvolvimento moderno em C# (.NET), que permite criar aplicações com baixo acoplamento, testáveis e de fácil manutenção. No ecossistema .NET, a principal ferramenta para implementar DI é o container integrado do pacote Microsoft.Extensions.DependencyInjection. Neste artigo, vamos detalhar o que é um container DI, como configurá-lo, quais ciclos de vida existem e como usá-lo corretamente em projetos reais.
O que é um container de Injeção de Dependência?
Um container de Injeção de Dependência (ou container IoC) é um componente de software que gerencia automaticamente a criação e injeção de dependências de objetos. Em vez de criar objetos manualmente com new, você registra os tipos no container, e ele cuida da criação e vinculação deles.
Principais vantagens de usar um container DI:
- Redução do acoplamento do código — as classes dependem de abstrações (interfaces), não de implementações concretas.
- Facilidade de teste — é fácil substituir dependências por objetos mock.
- Gerenciamento centralizado do ciclo de vida — você controla quando os objetos são criados e destruídos.
- Maior flexibilidade — é possível alterar implementações sem modificar o código dos consumidores.
Instalação e configuração do container DI
Em projetos .NET modernos (ASP.NET Core, Console, Worker Services), o container DI já está integrado. Para usá-lo, basta adicionar o pacote ao projeto:
dotnet add package Microsoft.Extensions.DependencyInjectionDepois disso, cria-se uma instância de IServiceCollection, onde todas as dependências são registradas. Vejamos um exemplo simples de registro de serviços:
using Microsoft.Extensions.DependencyInjection;
// Definimos a interface e sua implementaçãopublic interface IMessageService{ void Send(string message);}
public class EmailService : IMessageService{ public void Send(string message) { Console.WriteLine($"Email enviado: {message}"); }}
// Configuração do containervar services = new ServiceCollection();services.AddTransient<IMessageService, EmailService>();
// Construímos o provedor de serviçosvar serviceProvider = services.BuildServiceProvider();
// Obtemos uma instância do serviçovar messageService = serviceProvider.GetService<IMessageService>();messageService.Send("Olá do DI!");Ciclos de vida das dependências
O container DI suporta três modos principais de gerenciamento do tempo de vida dos objetos:
- Transient (AddTransient) — cada solicitação cria uma nova instância. Adequado para serviços leves e sem estado.
- Scoped (AddScoped) — uma instância por escopo (por exemplo, por requisição HTTP no ASP.NET Core). Ideal para serviços com estado dentro de uma única operação.
- Singleton (AddSingleton) — uma única instância para toda a aplicação. Usado para caches, loggers, configurações.
Exemplo de registro com diferentes ciclos de vida:
services.AddTransient<ITransientService, TransientService>();services.AddScoped<IScopedService, ScopedService>();services.AddSingleton<ISingletonService, SingletonService>();Importante: se um serviço depende de outro serviço com um ciclo de vida mais curto, isso pode causar erros (por exemplo, um Singleton que usa um Scoped). Essas situações são chamadas de "dependências cativas" e devem ser evitadas.
Injeção de dependências pelo construtor
A forma mais comum de injeção é pelo construtor. O container resolve automaticamente todas as depend