Skip to main content

🏭 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 LifetimeDescriçãoQuando o Factory é Executado
SingletonCriado uma única vez durante toda a vida da aplicaçãoO factory é executado apenas uma vez, na primeira resolução
ScopedCriado uma vez por escopo (geralmente por requisição HTTP)O factory é executado a cada novo escopo, ou seja, a cada requisição
TransientCriado toda vez que for solicitadoO 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, HttpContext ou 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

  1. 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.

  2. 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 Scoped ou Transient, pois isso impacta diretamente a performance.

  3. 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.

  4. Dependências Scoped em Singletons causam erro. Se o factory de um Singleton tentar resolver um serviço Scoped, 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):

TipoExecução esperadaObservação
SingletonExibe a mensagem uma única vez (na primeira requisição)Mesma instância reaproveitada
ScopedExibe a mensagem uma vez por requisiçãoNova instância a cada HttpContext
TransientExibe a mensagem toda vez que o serviço é resolvidoPode 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, IOptionsSnapshot fornecerá a configuração atualizada para aquele escopo.

✅ Conclusão

PontoExplicação
🏭 O factory é o método de construção do serviçoEle define como o objeto será criado
🔁 O lifetime define quando o factory é executadoSingleton → 1x / Scoped → por escopo / Transient → toda resolução
⚙️ Lógica no factory deve ser usada com cuidadoEvite operações pesadas, pois serão repetidas conforme o ciclo de vida
💡 Use IOptionsSnapshot em fábricas ScopedGarante que o serviço receba a configuração atualizada a cada requisição