🏭 Factory Methods e Ciclo de Vida de Serviços no .NET | Csharp
- 🏭 Factory Methods e Ciclo de Vida de Serviços no .NET | Csharp
📘 Visão Geral
No .NET, quando você registra um serviço no container de injeção de dependência (DI), pode fazê-lo de várias formas, sendo uma delas o uso de um factory method — uma função que define como o objeto será criado.
Por exemplo:
services.AddScoped<IMeuServico>(provider =>
{
var dependency = provider.GetRequiredService<IDependency>();
return new MeuServico(dependency);
});
Esse padrão é extremamente poderoso, pois permite personalizar a criação da instância, incluir lógica adicional ou mesmo inicializações dinâmicas baseadas em outros serviços ou em contexto.
Porém, o momento em que o factory é executado depende diretamente do ciclo de vida (lifetime) configurado para o serviço.
🔁 Ciclo de Vida e Execução do Factory
O .NET define três principais ciclos de vida para serviços:
| Tipo de Lifetime | Descrição | Quando o Factory é Executado |
|---|---|---|
| Singleton | Criado uma única vez durante toda a vida da aplicação | O factory é executado apenas uma vez, na primeira resolução |
| Scoped | Criado uma vez por escopo (geralmente por requisição HTTP) | O factory é executado a cada novo escopo, ou seja, a cada requisição |
| Transient | Criado toda vez que for solicitado | O factory é executado toda vez que o serviço é injetado ou resolvido |
⚙️ Exemplo Prático: Factory com Lógica Customizada
services.AddScoped<IMeuServico>(provider =>
{
Console.WriteLine("Executando factory de IMeuServico...");
var config = provider.GetRequiredService<IOptions<AppConfig>>().Value;
// Exemplo de lógica dentro do factory:
if (config.ModoDebug)
{
Console.WriteLine("Instanciando em modo DEBUG");
}
return new MeuServico(config.NomeAplicacao);
});
🧩 O que acontece em cada cenário:
-
Se registrado como
Scoped:- A função factory acima será executada a cada requisição HTTP (ou escopo manual).
- Isso significa que a lógica dentro do factory será reexecutada todas as vezes.
- É ideal quando o serviço depende de algo que varia por escopo, como
DbContext,HttpContextou informações de autenticação.
-
Se registrado como
Singleton:- O factory será executado apenas uma vez, na primeira injeção.
- Qualquer lógica dentro dele (como logs ou inicialização) será executada apenas uma vez.
- Esse comportamento é ideal para serviços que não têm dependências de escopo e que devem persistir por toda a aplicação.
-
Se registrado como
Transient:- O factory será executado toda vez que o serviço for resolvido, mesmo dentro da mesma requisição.
- Isso pode causar reexecuções excessivas da lógica, especialmente se o serviço for injetado em múltiplos lugares.
🧠 Considerações Importantes
-
O factory é parte do ciclo de vida do serviço. Ele é o “método de construção” usado pelo container. Portanto, sua execução segue o mesmo padrão do lifetime escolhido.
-
Lógica dentro do factory deve ser leve. Evite operações pesadas, como chamadas a banco ou I/O, dentro do factory — especialmente se o lifetime for
ScopedouTransient, pois isso impacta diretamente a performance. -
Evite efeitos colaterais repetitivos. Se a lógica dentro do factory gera logs, inicializa conexões ou cria arquivos, lembre-se de que isso ocorrerá toda vez que o factory rodar, conforme o ciclo de vida.
-
Dependências Scoped em Singletons causam erro. Se o factory de um
Singletontentar resolver um serviçoScoped, o .NET lançará uma exceção (InvalidOperationException), pois o escopo não existe no contexto singleton.
🔍 Exemplo Comparativo
// Singleton
services.AddSingleton<IMeuServico>(provider =>
{
Console.WriteLine("Factory executado: SINGLETON");
return new MeuServico();
});
// Scoped
services.AddScoped<IMeuServico>(provider =>
{
Console.WriteLine("Factory executado: SCOPED");
return new MeuServico();
});
// Transient
services.AddTransient<IMeuServico>(provider =>
{
Console.WriteLine("Factory executado: TRANSIENT");
return new MeuServico();
});
🧾 Resultado esperado (em uma aplicação web):
| Tipo | Execução esperada | Observação |
|---|---|---|
Singleton | Exibe a mensagem uma única vez (na primeira requisição) | Mesma instância reaproveitada |
Scoped | Exibe a mensagem uma vez por requisição | Nova instância a cada HttpContext |
Transient | Exibe a mensagem toda vez que o serviço é resolvido | Pode ser várias vezes por requisição |
💡 Dica: Factory + Options + Scoped
Se o factory consome configurações via IOptions<T> e o serviço for Scoped, a configuração será reavaliada a cada escopo, mas não recarregada automaticamente (a menos que você use IOptionsSnapshot<T>).
Exemplo:
services.AddScoped<IMeuServico>(provider =>
{
var options = provider.GetRequiredService<IOptionsSnapshot<AppConfig>>();
return new MeuServico(options.Value);
});
Nesse caso:
- A cada requisição, o factory será executado novamente;
- A cada execução,
IOptionsSnapshotfornecerá a configuração atualizada para aquele escopo.
✅ Conclusão
| Ponto | Explicação |
|---|---|
| 🏭 O factory é o método de construção do serviço | Ele define como o objeto será criado |
| 🔁 O lifetime define quando o factory é executado | Singleton → 1x / Scoped → por escopo / Transient → toda resolução |
| ⚙️ Lógica no factory deve ser usada com cuidado | Evite operações pesadas, pois serão repetidas conforme o ciclo de vida |
💡 Use IOptionsSnapshot em fábricas Scoped | Garante que o serviço receba a configuração atualizada a cada requisição |