Program.cs, serviços e métodos de extensão
ResumoO
Program.csé o ponto de entrada e o lugar onde registramos serviços (no contêiner de DI) e montamos o pipeline HTTP. Para não deixá-lo gigante, extraímos os registros para métodos de extensão sobreIServiceCollection.
1. Objetivos de Aprendizagem#
- Entender as duas fases do
Program.cs: registrar serviços e construir o pipeline. - Registrar serviços com os tempos de vida corretos (
Singleton,Scoped,Transient). - Organizar a configuração em métodos de extensão reutilizáveis.
2. Pré-requisitos#
- Projeto
Catalog.Apicriado (tópico 16.1).
3. Conceito#
Desde o .NET 6, o Program.cs usa top-level statements e o WebApplicationBuilder. Há duas fases claras:
- Antes de
builder.Build()— você adiciona serviços aobuilder.Services(o contêiner de DI). - Depois de
builder.Build()— você configura o pipeline de middleware doappe chamaapp.Run().
O porquê dos métodos de extensão: conforme o projeto cresce, o Program.cs acumula dezenas de linhas de registro. Extrair isso para ServiceExtensions melhora legibilidade, testabilidade e reuso — um princípio direto de separação de responsabilidades.
4. Mão na Massa#
4.1. Setup#
Nenhum pacote novo. Vamos apenas reorganizar o Program.cs.
4.2. Implementação Passo a Passo#
- Crie a pasta
Extensions/e o arquivoServiceExtensions.cs:
namespace Catalog.Api.Extensions;
public static class ServiceExtensions
{
public static IServiceCollection ConfigureControllers(this IServiceCollection services)
{
services.AddControllers();
return services;
}
}
- Registre serviços com o tempo de vida adequado. A regra prática:
AddSingleton— uma instância para toda a aplicação (ex.: cache em memória, configuração imutável).AddScoped— uma instância por requisição HTTP (ex.:DbContext, repositórios).AddTransient— uma nova instância a cada resolução (serviços leves e sem estado).
- Consuma a extensão no
Program.cs(veja o Exemplo Completo).
4.3. Executando#
dotnet run
A aplicação sobe normalmente; a diferença está na organização do código, não no comportamento.
5. Exemplo Completo#
Program.cs:
using Catalog.Api.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Fase 1: registro de serviços
builder.Services.ConfigureControllers();
var app = builder.Build();
// Fase 2: pipeline HTTP
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
| Extrair registros para métodos de extensão coesos | Um Program.cs com 200 linhas de registro |
Escolher o tempo de vida certo (DbContext é Scoped) | Injetar um serviço Scoped dentro de um Singleton (captured dependency) |
Retornar IServiceCollection para encadear chamadas | Métodos de extensão que não compõem |
Atençãoinjetar um serviço
Scoped(comoDbContext) dentro de umSingletongera a captive dependency — o serviço scoped vive além da requisição e causa bugs difíceis. O .NET detecta muitos desses casos no startup quandoValidateScopesestá ativo (padrão em Development).
7. Segurança e Produção#
- Mantenha
UseDeveloperExceptionPageapenas emDevelopment; em produção use o middleware global de erros (Módulo 19). - A ordem do pipeline importa:
UseAuthenticationdeve vir antes deUseAuthorization.
8. Exercícios#
- Fácil: crie
ConfigureCorscomo método de extensão e chame-o noProgram.cs. - Médio: registre um serviço
IDateTimeProvidercomoSingletone injete-o num controller. - Desafio: provoque de propósito uma captive dependency e observe a exceção de validação de escopo no startup; explique a mensagem.
9. Resumo#
O Program.cs registra serviços e monta o pipeline. Métodos de extensão sobre IServiceCollection mantêm essa classe enxuta, e a escolha correta do tempo de vida evita bugs de ciclo de vida.
10. Próximos Passos#
A seguir configuramos CORS, integração com IIS e comportamento por ambiente.
11. Referências#
- Microsoft Learn — Injeção de dependência no ASP.NET Core
- Microsoft Learn — Tempos de vida de serviço
- Microsoft Learn — WebApplication e WebApplicationBuilder