Software · Mapas de Conteúdo & Guias
.NET Framework 4.8.1 vs .NET 10: O Guia Definitivo de Modernização
Comparativo arquitetural detalhado entre o .NET Framework 4.8.1 e o .NET 10: 36 conceitos práticos, quebras de paradigma, evolução do C# 14 e estratégias de migração.
Aplicações corporativas construídas sobre o .NET Framework 4.8.1 continuam ativas em muitas empresas, mas enfrentam limites claros de infraestrutura e escalabilidade. Projetado para rodar em servidores Windows sob o controle do Internet Information Services (IIS), o modelo clássico carrega dependências profundas de bibliotecas do sistema operacional, configurações extensas em XML e um ciclo de vida síncrono que não conversa bem com a computação em nuvem atual.
O .NET 10 representa o estágio mais maduro da unificação da plataforma: um runtime multiplataforma com suporte de longo prazo (LTS), execução direta no servidor Kestrel, injeção de dependências embutida e compilação Ahead-of-Time (Native AOT).
Migrar entre esses dois ecossistemas exige mais do que atualizar pacotes NuGet ou ajustar a versão do projeto. As aplicações modernas gerenciam memória de outra forma, resolvem requisições HTTP por pipelines funcionais e operam com contêineres mínimos.
Para documentar essas diferenças de maneira tangível e verificável em código, organizei o repositório aberto dotnet-modernization-lab. Este guia reúne os 36 conceitos do projeto, agrupados em 10 frentes técnicas, analisando como as decisões de engenharia mudaram entre o modelo clássico e o runtime moderno.
flowchart TD
subgraph Legado["Ecossistema Clássico (.NET Framework 4.8.1)"]
A1["Hospedagem acoplada ao IIS e Windows"]
A2["Ciclo rígido de eventos via HttpApplication"]
A3["Configuração monolítica em XML no Web.config"]
A4["Entity Framework 6 com EDMX e Lazy Loading"]
A5["Serialização dinâmica dependente de reflexão"]
end
subgraph Moderno["Runtime Unificado (.NET 10 LTS)"]
B1["Kestrel embutido e contêineres Linux mínimos"]
B2["Pipeline assíncrono linear de middlewares"]
B3["Options Pattern tipado com recarregamento a quente"]
B4["EF Core 10 com operações em lote e colunas JSON"]
B5["System.Text.Json e compilação nativa AOT"]
end
Legado ==>|"Modernização gradual com Strangler Fig"| Moderno
classDef legacy fill:#1f131a,stroke:#f43f5e,stroke-width:1px,color:#f8fafc;
classDef modern fill:#0f2724,stroke:#34d399,stroke-width:2px,color:#f8fafc;
class A1,A2,A3,A4,A5 legacy;
class B1,B2,B3,B4,B5 modern;
As 10 Grandes Quebras de Paradigma
A evolução da plataforma ocorreu pela substituição planejada de convenções antigas por abstrações mais enxutas. Compreender os dez pontos centrais dessa transição ajuda a orientar o planejamento técnico das equipes.
1. Exclusividade Windows e IIS versus Multiplataforma com Kestrel
No .NET Framework 4.8.1, o código web depende diretamente da biblioteca System.Web.dll, que chama rotinas da API Win32 e delega a execução ao processo de trabalho do IIS (w3wp.exe). Essa arquitetura exige servidores Windows completos. Quando empacotadas em Docker, as imagens para Windows Server Core variam entre 6 GB e 12 GB, demoram minutos para subir e encarecem a operação em nuvem.
No .NET 10, o aplicativo web roda como um executável de console comum que inicializa o servidor Kestrel interno. O mesmo código funciona de maneira uniforme em Linux (Ubuntu, Debian, Alpine ou imagens chiseled), macOS e Windows. Contêineres Linux chiseled pesam menos de 45 MB, reduzem a superfície de ataque por não incluírem shell e iniciam em frações de segundo.
2. Ciclo de Vida do HttpApplication versus Pipeline Linear de Middlewares
No ASP.NET clássico, a requisição percorria uma máquina de estados com 23 eventos gerenciados pelo IIS (como BeginRequest, AuthenticateRequest e EndRequest). Desenvolvedores precisavam implementar IHttpModule e consultar o singleton estático HttpContext.Current, que ficava preso à thread em execução e tornava a escrita de testes unitários isolados bastante trabalhosa.
No .NET 10, o fluxo de requisição é um encadeamento linear de delegates assíncronos (RequestDelegate). Cada middleware recebe a instância de HttpContext diretamente via injeção, executa sua lógica prévia, invoca await next() e trata a resposta no retorno. O desenvolvedor monta o pipeline no próprio código, com controle exato sobre a ordem de execução e a possibilidade de encerrar a resposta quando necessário.
3. Falta de Injeção de Dependências versus Contêiner Nativo
O .NET Framework 4.8.1 não incluía um mecanismo de injeção de dependências na biblioteca base. As equipes recorriam a contêineres externos (como Autofac, Unity ou Ninject) e configuravam adaptadores manuais no IDependencyResolver do MVC ou da Web API. Era frequente encontrar o antipadrão Service Locator resolvendo instâncias estáticas no meio do código de negócio.
O .NET 10 traz um contêiner nativo integrado desde a inicialização (Microsoft.Extensions.DependencyInjection), com três ciclos de vida formais:
- Transient: nova instância a cada solicitação.
- Scoped: uma instância por requisição HTTP.
- Singleton: instância única preservada durante toda a vida do processo.
A plataforma também oferece suporte a Keyed Services ([FromKeyedServices("nome")]), permitindo distinguir implementações da mesma interface diretamente pelo nome, sem plugins externos.
4. Configuração em XML versus Hierarquia JSON e Options Pattern
O arquivo Web.config centralizava regras de compilação, redirecionamentos de bibliotecas com <bindingRedirect> e chaves de negócio em <appSettings>. A leitura dependia de conversões manuais de string propensas a erro, como ConfigurationManager.AppSettings["Taxa"]. Qualquer edição no arquivo em produção disparava a reciclagem do processo no IIS, derrubando requisições em andamento.
No .NET 10, as configurações combinam múltiplas fontes: arquivos JSON (appsettings.json, appsettings.{Ambiente}.json), variáveis de ambiente, argumentos de linha de comando e cofres como Azure Key Vault. Os valores são mapeados para classes fortemente tipadas por meio do Options Pattern (IOptions<T>, IOptionsSnapshot<T> e IOptionsMonitor<T>), com validação no início da aplicação via DataAnnotations e recarregamento sem reinício do processo.
5. MSBuild Verboso versus SDK-Style Conciso
Os arquivos .csproj clássicos utilizavam a sintaxe tradicional do MSBuild, listando manualmente cada arquivo fonte do projeto (<Compile Include="Services\UsuarioService.cs" />). Essa abordagem gerava conflitos frequentes no Git quando vários desenvolvedores adicionavam arquivos em branches separadas.
O formato moderno SDK-Style utiliza convenções automáticas de inclusão de arquivos (globbing):
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
Todos os arquivos C# presentes no diretório são compilados automaticamente. O gerenciamento de pacotes NuGet adota referências transitivas com <PackageReference>, dispensando pastas packages/ pesadas no repositório.
6. Limitações do C# 7.3 versus o C# 14
O .NET Framework 4.8.1 suporta oficialmente até o C# 7.3. As inovações da linguagem que exigem novos tipos em runtime (como Span<T> e tipos anuláveis por referência) não puderam ser integradas ao framework clássico, deixando o código legado mais prolixo e dependente de checagens manuais de nulidade defensiva.
O .NET 10 opera com o C# 14, trazendo sintaxes mais diretas e seguras:
- Top-Level Statements: removem a classe
Programe o métodoMainestático. - Primary Constructors: simplificam a injeção de dependências na própria assinatura de classes e records.
- Nullable Reference Types: habilitam a verificação estática de possíveis referências nulas em tempo de compilação.
- Pattern Matching e Collection Expressions: permitem manipular dados e inicializar coleções (
[1, 2, 3]) com clareza sintática.
7. Newtonsoft.Json versus System.Text.Json com Source Generators
Por muitos anos, o Newtonsoft.Json foi a biblioteca padrão de serialização no .NET Framework. Embora funcional, ela foi projetada antes das estruturas de memória contígua e faz uso intenso de reflexão dinâmica em tempo de execução, gerando alocações expressivas de objetos na memória Heap.
No .NET 10, a serialização é executada pelo namespace System.Text.Json, projetado sobre buffers UTF-8 diretos. Com o uso de Source Generators, a lógica de conversão é gerada durante a compilação, eliminando o custo de reflexão e viabilizando o modo Native AOT. Além disso, serializadores inseguros como o BinaryFormatter foram removidos permanentemente por razões de segurança.
8. Entity Framework 6 versus EF Core 10
No ecossistema legado, o acesso a dados corporativo concentrou-se no Entity Framework 6. Muitas bases dependiam de arquivos visuais .edmx propensos a corrupção em mesclagens do Git. O carregamento preguiçoso (Lazy Loading) vinha ativado por padrão, gerando consultas em cascata (N+1 queries), e a atualização de registros em lote exigia carregar todas as entidades na memória antes de salvar.
O EF Core 10 reformulou esses fluxos:
- Operações em lote nativas:
ExecuteUpdateAsync()eExecuteDeleteAsync()geram comandos SQL diretos sem manter entidades no rastreador de mudanças. - Colunas JSON: mapeamento de propriedades ricas diretamente para tipos JSON de bancos relacionais, com suporte a consultas LINQ que traduzem para funções nativas do banco.
- Split Queries (
AsSplitQuery): evitam a duplicação cartesiana de dados em consultas relacionais com múltiplos cruzamentos. - Modelos compilados: aceleram a inicialização fria da aplicação.
9. Compilação JIT Tradicional versus Native AOT
No .NET Framework 4.8.1, o código C# é transformado em instruções intermediárias (IL) e compilado para código de máquina pelo compilador JIT (Just-in-Time) no momento da primeira execução de cada método. O gerador de imagens nativas (NGen) dependia da instalação local do framework completo e do Global Assembly Cache (GAC), o que limitava seu uso em contêineres efêmeros.
O .NET 10 consolida a compilação Native AOT (<PublishAot>true</PublishAot>). O código da aplicação e as rotinas essenciais do runtime são traduzidos diretamente para um binário executável específico da plataforma de destino (ELF no Linux, PE no Windows, Mach-O no macOS). O compilador remove o código não utilizado (trimming), alcançando tempos de inicialização inferiores a 15 milissegundos e consumo de memória entre 10 MB e 18 MB.
10. Diagnósticos no Windows versus OpenTelemetry Nativo
Monitorar aplicações no .NET Framework 4.8.1 dependia de contadores de desempenho do Windows (PerfMon), do Visualizador de Eventos e de agentes externos que injetavam DLLs no processo CLR. Não existia um padrão corporativo unificado para correlacionar chamadas entre microsserviços sem cabeçalhos customizados.
No .NET 10, a observabilidade adota o padrão aberto OpenTelemetry e a especificação W3C Trace Context. A biblioteca base inclui as classes ActivitySource e System.Diagnostics.Metrics. A coleta de métricas, rastreamentos e logs estruturados é nativa, enviando dados via protocolo OTLP para ferramentas de monitoramento como Prometheus, Jaeger e o painel do .NET Aspire sem penalizar a vazão da aplicação.
Matriz Geral dos 36 Conceitos (Módulos A a J)
O repositório dotnet-modernization-lab divide essas transformações em 10 módulos técnicos contendo 36 conceitos isolados. Cada conceito traz projetos comparativos funcionais que exemplificam a abordagem legada e o padrão moderno equivalente.
graph TD
Guia["Guia de Modernização .NET: 36 Conceitos em 10 Módulos"]
MA["Módulo A: Bootstrap e Estrutura (01-07)"]
MB["Módulo B: Dados e Persistência (08-10)"]
MC["Módulo C: Segurança e Identidade (11-13)"]
MD["Módulo D: APIs, HTTP e Tempo Real (14-17)"]
ME["Módulo E: Logging e Observabilidade (18-20)"]
MF["Módulo F: Performance, Cache e JSON (21-23)"]
MG["Módulo G: Linguagem C# e Assincronismo (24-25)"]
MH["Módulo H: Arquitetura e Resiliência (26-29)"]
MI["Módulo I: Deploy, Contêineres e Nuvem (30-31)"]
MJ["Módulo J: Recursos Exclusivos do .NET 10 (32-36)"]
Guia --> MA
Guia --> MB
Guia --> MC
Guia --> MD
Guia --> ME
Guia --> MF
Guia --> MG
Guia --> MH
Guia --> MI
Guia --> MJ
classDef guia fill:#0f172a,stroke:#38bdf8,stroke-width:2px,color:#f8fafc;
classDef mod fill:#1e293b,stroke:#f59e0b,stroke-width:1px,color:#f8fafc;
class Guia guia;
class MA,MB,MC,MD,ME,MF,MG,MH,MI,MJ mod;
A seguir, analisamos cada um dos módulos e seus respectivos conceitos práticos.
Módulo A: Fundamentos da Plataforma e Bootstrap
O primeiro módulo trata da inicialização da aplicação, gerenciamento de configurações e estrutura do pipeline web.
| ID | Conceito Técnico | Abordagem no .NET Framework 4.8.1 | Abordagem no .NET 10 Moderno |
|---|---|---|---|
| 01 | Estrutura de Projeto e Bootstrap | Inicialização pelo Global.asax.cs com Application_Start, dependência do IIS e MSBuild XML clássico. |
Arquivo Program.cs com Top-Level Statements, servidor Kestrel embutido e SDK-style limpo. |
| 02 | Configuração e Options | Leitura estática via ConfigurationManager, seções XML em Web.config e transformações XDT. |
IConfiguration com hierarquia JSON, Options Pattern tipado, validação e recarregamento sem reinício. |
| 03 | Injeção de Dependências | Ausência de contêiner nativo no framework, dependência de bibliotecas externas e Service Locator. | Contêiner nativo IServiceCollection, ciclos Transient, Scoped, Singleton e Keyed Services. |
| 04 | Pipeline HTTP e Middlewares | Classes IHttpModule e IHttpHandler acopladas a eventos síncronos do IIS e HttpContext.Current. |
Pipeline linear assíncrono composto por delegates RequestDelegate encadeados com app.Use(). |
| 05 | Roteamento Web | Tabelas de rotas isoladas entre MVC (RouteConfig) e Web API (WebApiConfig). |
Endpoint Routing unificado na infraestrutura do ASP.NET Core com suporte a MapGroup. |
| 06 | Controllers e Ações | Separação entre classes base System.Web.Mvc.Controller e System.Web.Http.ApiController. |
Unificação em ControllerBase, atributo [ApiController] e retornos fortemente tipados via TypedResults. |
| 07 | Views, Razor e Templating | Razor v3 clássico, métodos auxiliares @Html.* síncronos e Child Actions bloqueantes. |
Tag Helpers semânticos (ex: diretiva asp-for), View Components e Razor Pages. |
Comparativo de Código: Inicialização da Aplicação (Conceito 01)
No modelo legado, a inicialização dependia do arquivo Global.asax.cs:
// .NET Framework 4.8.1 - Global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
No .NET 10, o registro de serviços e a montagem do pipeline ficam explícitos no Program.cs:
// .NET 10 - Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApi();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Módulo B: Dados, Persistência e Validação
Este módulo aborda mapeamento objeto-relacional, tratamento de contratos e persistência em banco.
| ID | Conceito Técnico | Abordagem no .NET Framework 4.8.1 | Abordagem no .NET 10 Moderno |
|---|---|---|---|
| 08 | Entity Framework: EF 6 vs EF Core 10 | Mapeamentos .edmx visuais, consultas lentas e ausência de operações nativas em lote. |
EF Core 10 com ExecuteUpdateAsync, ExecuteDeleteAsync, Split Queries e colunas JSON. |
| 09 | Padrão Repositório e Unit of Work | Repositórios genéricos anêmicos vazando IQueryable e criando abstrações excessivas sobre o ORM. | Uso direto do DbContext gerenciado por DI, Domain Repositories pontuais e Vertical Slices. |
| 10 | Validação e ProblemDetails | Checagem manual de ModelState.IsValid e formatos de erro proprietários em cada controller. |
Validação automática via DataAnnotations com respostas padronizadas pela RFC 7807/9457. |
Operações em Lote no EF Core 10 (Conceito 08)
No EF 6, atualizar múltiplos registros exigia carregar todas as entidades na memória:
// .NET Framework 4.8.1 - Carrega registros na memória antes de atualizar
using (var db = new LegacyDbContext())
{
var inativos = db.Clientes.Where(c => c.UltimoAcesso < dataLimite).ToList();
foreach (var cliente in inativos)
{
cliente.Ativo = false;
}
db.SaveChanges(); // Dispara instruções UPDATE individuais
}
No EF Core 10, a atualização é traduzida para uma única instrução SQL executada diretamente no banco:
// .NET 10 - Atualização direta sem carregar entidades na memória
await dbContext.Clientes
.Where(c => c.UltimoAcesso < dataLimite)
.ExecuteUpdateAsync(setter => setter.SetProperty(c => c.Ativo, false));
Módulo C: Autenticação, Autorização e Segurança
Este módulo analisa a gestão de identidade, proteção contra ameaças comuns e descontinuação de protocolos inseguros.
| ID | Conceito Técnico | Abordagem no .NET Framework 4.8.1 | Abordagem no .NET 10 Moderno |
|---|---|---|---|
| 11 | Autenticação | FormsAuthentication, dependência da MachineKey do IIS e cookies proprietários. |
AddAuthentication modular com suporte simultâneo a Cookies, JWT Bearer e OpenID Connect. |
| 12 | Autorização e Políticas | Atributos rígidos com papéis fixos ([Authorize(Roles = "Admin")]) e regras dispersas. |
AddAuthorization baseado em políticas com requisitos e handlers desacoplados e testáveis. |
| 13 | Práticas de Segurança | Validação CSRF manual, cabeçalhos em Web.config e risco de vulnerabilidades com BinaryFormatter. |
UseAntiforgery nativo, CORS tipado, Security Headers em middleware e remoção de serializadores legados. |
Módulo D: APIs, Conexões HTTP e Tempo Real
Este módulo foca na exposição de endpoints de rede, consumo resiliente de serviços externos e mensageria bidirecional.
| ID | Conceito Técnico | Abordagem no .NET Framework 4.8.1 | Abordagem no .NET 10 Moderno |
|---|---|---|---|
| 14 | Construção de APIs REST | Herança obrigatória de ApiController, rotas manuais em XML e contratos ad-hoc. |
Minimal APIs de alta velocidade ou controllers com OpenAPI nativo e TypedResults. |
| 15 | HttpClient e Resiliência | Criação de new HttpClient() dentro de blocos using, causando esgotamento de portas de socket. |
IHttpClientFactory, SocketsHttpHandler e integração declarativa com Microsoft Resilience e Polly. |
| 16 | Filtros de Ação | Classes derivadas de ActionFilterAttribute sem injeção de dependência e com estado estático. |
IAsyncActionFilter, filtros tipados via ServiceFilter e Endpoint Filters em Minimal APIs. |
| 17 | Comunicação em Tempo Real | SignalR 2.x rodando sobre Katana/OWIN com dependência de jQuery e proxies dinâmicos no cliente. | ASP.NET Core SignalR com hubs fortemente tipados, serialização MessagePack e cliente sem jQuery. |
Prevenção de Esgotamento de Sockets com HttpClient (Conceito 15)
Criar instâncias de HttpClient dentro de blocos using causa o acúmulo de conexões em estado TIME_WAIT:
// .NET Framework 4.8.1 - Prática incorreta que consome portas efêmeras do sistema
public async Task<string> ObterDadosLegadoAsync(string url)
{
using (var client = new HttpClient())
{
return await client.GetStringAsync(url);
} // A porta TCP permanece retida pelo sistema operacional por minutos
}
No .NET 10, o IHttpClientFactory reaproveita pools de conexão e adiciona regras de resiliência:
// .NET 10 - Cliente tipado com tratamento automático de retries e timeout
builder.Services.AddHttpClient<CotacaoClient>(client =>
{
client.BaseAddress = new Uri("https://api.parceiro.com.br/");
client.Timeout = TimeSpan.FromSeconds(5);
}).AddStandardResilienceHandler();
Módulo E: Logging e Observabilidade
Este módulo estabelece padrões de diagnóstico, auditoria e emissão de telemetria em produção.
| ID | Conceito Técnico | Abordagem no .NET Framework 4.8.1 | Abordagem no .NET 10 Moderno |
|---|---|---|---|
| 18 | Logging Estruturado | System.Diagnostics.Trace ou log4net com strings concatenadas que dificultam consultas. |
ILogger com logging estruturado e Source Generators ([LoggerMessage]) sem alocação de memória. |
| 19 | Tratamento Global de Erros | Captura em Application_Error no Global.asax ou páginas HTML estáticas de customErrors. |
Middleware UseExceptionHandler, implementação de IExceptionHandler e formato RFC 7807. |
| 20 | Health Checks e Sondas | Páginas .ashx rudimentares sem padronização de estados de ciclo de vida. |
AddHealthChecks com sondas de Liveness e Readiness prontas para orquestradores Kubernetes. |
Módulo F: Desempenho, Serialização e Cache
Este módulo foca na redução de alocações de memória e na gestão eficiente de dados em cache.
| ID | Conceito Técnico | Abordagem no .NET Framework 4.8.1 | Abordagem no .NET 10 Moderno |
|---|---|---|---|
| 21 | Serialização JSON | Newtonsoft.Json com uso intenso de reflexão e alocações frequentes na memória Heap. |
System.Text.Json com Source Generators em tempo de compilação e suporte a Native AOT. |
| 22 | Estratégias de Caching | HttpRuntime.Cache em memória local sem tags de invalidação nem controle de concorrência. |
IMemoryCache, IDistributedCache e Output Cache com tags para invalidação direcionada. |
| 23 | Otimizações de Baixo Nível | Fatiamento com Substring(), conversões por boxing e alocações repetidas de arrays. |
Span, ReadOnlySpan, Memory e ArrayPool para operações sem alocação de memória. |
Processamento de Dados com Zero Alocação de Heap (Conceito 23)
No .NET Framework, fatiar strings cria uma nova instância de objeto na memória Heap a cada chamada:
// .NET Framework 4.8.1 - Aloca uma nova string para cada segmento
string cabecalho = "ORDER-ID:982341-BR";
string codigo = cabecalho.Substring(9, 6);
int id = int.Parse(codigo);
No .NET 10, o tipo ReadOnlySpan<char> cria uma visão direta sobre os dados existentes:
// .NET 10 - Parsing direto sobre a memória sem nova alocação
ReadOnlySpan<char> cabecalhoSpan = "ORDER-ID:982341-BR".AsSpan();
ReadOnlySpan<char> codigoSpan = cabecalhoSpan.Slice(9, 6);
int id = int.Parse(codigoSpan);
Módulo G: Linguagem C# e Programação Assíncrona
Este módulo analisa os ganhos de produtividade da linguagem e a eliminação de travamentos assíncronos.
| ID | Conceito Técnico | Abordagem no .NET Framework 4.8.1 | Abordagem no .NET 10 Moderno |
|---|---|---|---|
| 24 | Evolução da Linguagem C# | Estagnação no C# 7.3 com classes prolixas e construtores manuais. | C# 14 com Primary Constructors, Records, Collection Expressions, Pattern Matching e campo field. |
| 25 | Async, Await e Streaming | Presença do AspNetSynchronizationContext, que gerava deadlocks ao chamar .Result. |
Execução direta no ThreadPool, retorno leve via ValueTask e streaming contínuo com IAsyncEnumerable. |
Prevenção de Deadlocks por SynchronizationContext (Conceito 25)
No ASP.NET clássico, cada requisição mantinha um AspNetSynchronizationContext. Chamar .Result de forma síncrona bloqueava a thread e impedia a conclusão da tarefa:
// .NET Framework 4.8.1 - Causa deadlock no IIS
public ActionResult ObterRelatorio()
{
// A thread do IIS aguarda o resultado, mas a continuação tenta retornar para a mesma thread
var relatorio = ServicoAssincrono().Result;
return View(relatorio);
}
O ASP.NET Core eliminou o SynchronizationContext, permitindo que as continuações retomem em qualquer thread livre. O streaming de dados volumosos também pode ser entregue de forma progressiva via IAsyncEnumerable<T>:
// .NET 10 - Entrega progressiva de dados ao cliente
[HttpGet("stream")]
public async IAsyncEnumerable<ItemRelatorio> StreamRelatorioAsync()
{
await foreach (var item in repositorio.ObterItensEmStreamAsync())
{
yield return item;
}
}
Módulo H: Padrões Arquiteturais e Resiliência
Este módulo aborda a estruturação de serviços, segregação de responsabilidades e execução em segundo plano.
| ID | Conceito Técnico | Abordagem no .NET Framework 4.8.1 | Abordagem no .NET 10 Moderno |
|---|---|---|---|
| 26 | SOLID e Clean Architecture | Monólitos em camadas tradicionais (N-Tier) com controllers acumulando regras de negócio. | Clean Architecture, separação rica de entidades, inversão de controle e Vertical Slice Architecture. |
| 27 | Serviços em Segundo Plano | HostingEnvironment.QueueBackgroundWorkItem sujeito a cancelamentos pelo IIS ou Windows Service separado. |
BackgroundService, IHostedService, PeriodicTimer e filas seguras com System.Threading.Channels. |
| 28 | Padrão Mediator e CQRS | Serviços de aplicação inflados agrupando consultas e comandos sem separação formal. | Separação de Commands e Queries com MediatR e Pipeline Behaviors para validação e auditoria. |
| 29 | Design de APIs e Contratos | Versionamento por caminhos manuais na URL e documentação técnica desatualizada. | Versionamento declarativo (Asp.Versioning), suporte nativo a OpenAPI e rate limiting integrado. |
Módulo I: Publicação, Contêineres e Ambientes
Este módulo examina o ciclo de build, empacotamento em contêineres e gestão de variáveis de ambiente.
| ID | Conceito Técnico | Abordagem no .NET Framework 4.8.1 | Abordagem no .NET 10 Moderno |
|---|---|---|---|
| 30 | Publicação e Deploy | Pacotes WebDeploy proprietários, scripts MSDeploy lentos e imagens Windows de 8 GB. | dotnet publish multiplataforma, contêineres Linux Chiseled abaixo de 45 MB e executáveis autocontidos. |
| 31 | Ambientes e Feature Flags | Arquivos de transformação Web.Debug.config e diretivas de compilação #if DEBUG. |
IWebHostEnvironment, hierarquia JSON por ambiente e alternância dinâmica com Microsoft.FeatureManagement. |
Módulo J: Recursos do .NET 10
Este módulo reúne capacidades introduzidas no .NET 10 que ampliam o desempenho e a observabilidade das aplicações.
| ID | Conceito Técnico | Descrição Técnica e Capacidade no .NET 10 |
|---|---|---|
| 32 | Minimal APIs Avançado | Endpoints diretos sem sobrecarga de alocação de controllers, com agrupamento de rotas (MapGroup), Endpoint Filters encadeados e retornos tipados via TypedResults. |
| 33 | Native AOT e Trimming | Compilação direta para código de máquina puro sem instruções intermediárias. Elimina o compilador JIT, acelera a inicialização para menos de 15 ms e reduz o consumo de RAM para a faixa de 10 MB a 15 MB. |
| 34 | HybridCache com Proteção de Stampede | Integração entre cache local L1 (memória) e distribuído L2 (Redis), com bloqueio atômico contra sobrecarga no banco quando chaves muito acessadas expiram simultaneamente. |
| 35 | OpenTelemetry e .NET Aspire | Rastreamento distribuído no padrão W3C Trace Context, métricas de vazão e exportação via OTLP sem necessidade de agentes proprietários instalados no servidor. |
| 36 | Blazor WebAssembly e Server | Construção de telas reativas em C#, eliminando a dependência do ASP.NET Web Forms legado e seu mecanismo pesado de __VIEWSTATE. |
Proteção contra Cache Stampede com HybridCache (Conceito 34)
Quando uma chave de cache muito requisitada expira, diversas chamadas simultâneas podem atacar o banco de dados ao mesmo tempo para recalcular o valor. O HybridCache do .NET 10 resolve esse problema sincronizando as requisições concorrentes de forma transparente:
// .NET 10 - Apenas uma chamada busca o registro no banco durante a expiração
app.MapGet("/api/produtos/{id}", async (int id, HybridCache cache, IProdutoRepository repo) =>
{
var produto = await cache.GetOrCreateAsync(
$"produto:{id}",
async cancelToken => await repo.ObterPorIdAsync(id, cancelToken),
options: new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(10),
LocalCacheExpiration = TimeSpan.FromMinutes(2)
},
tags: ["produtos", $"produto:{id}"]
);
return TypedResults.Ok(produto);
});
// Invalidação pontual por tag no cluster
app.MapPost("/api/produtos/{id}/inativar", async (int id, HybridCache cache) =>
{
await cache.RemoveByTagAsync($"produto:{id}");
return TypedResults.NoContent();
});
Tabela de Consulta Rápida (De ➔ Para)
A tabela abaixo resume a correspondência entre sintaxes e abordagens do .NET Framework 4.8.1 e do .NET 10 para apoiar refatorações no dia a dia:
| Operação Desejada | Como se fazia no .NET Framework 4.8.1 | Como se faz no .NET 10 Moderno |
|---|---|---|
| Ponto de entrada | Global.asax.cs com Application_Start |
Program.cs com Top-Level Statements |
| Definição de projeto | .csproj XML clássico com lista manual de arquivos |
.csproj no formato SDK-Style declarativo |
| Leitura de appSettings | ConfigurationManager.AppSettings["Chave"] |
builder.Configuration["Chave"] |
| String de conexão | ConfigurationManager.ConnectionStrings["Db"].ConnectionString |
builder.Configuration.GetConnectionString("Db") |
| Configurações tipadas | Classes herdando de ConfigurationSection |
Options Pattern tipado com builder.Services.Configure(options) |
| Serviço Scoped | Inexistente nativo (exigia Autofac ou Unity) | builder.Services.AddScoped(...) |
| Serviço Singleton | Inexistente nativo ou instâncias estáticas manuais | builder.Services.AddSingleton(...) |
| Injeção de dependência | Construtor tradicional via adaptadores do MVC/Web API | Primary Constructor direto na classe: public class Ctrl(ISvc svc) |
| Injeção com chave | Plugins de contêineres externos | Nativo com [FromKeyedServices("nome")] |
| Criação de endpoint HTTP | Classe herdando obrigatoriamente de ApiController |
Minimal API: app.MapGet("/itens", () => ...) |
| Agrupamento de rotas | Áreas do MVC ou rotas manuais no WebApiConfig | var grupo = app.MapGroup("/api/v1/pedidos"); |
| Interceptação de tráfego | IHttpModule acoplado ao evento BeginRequest |
Middleware funcional: app.Use(async (ctx, next) => ...) |
| Acesso ao contexto HTTP | Singleton estático HttpContext.Current |
Injetado explicitamente ou via IHttpContextAccessor |
| Retorno de JSON em API | return Ok(objeto); em ApiController |
return TypedResults.Ok(objeto); |
| Validação de modelo | Verificação manual com if (!ModelState.IsValid) |
Automático via [ApiController] emitindo RFC 7807 |
| Tratamento de exceções | Application_Error ou atributo HandleError |
app.UseExceptionHandler() e IExceptionHandler |
| Formulário com validação | @Html.TextBoxFor(m => m.Campo) no Razor |
Tag Helper: input com diretiva asp-for="Campo" |
| Consumo HTTP externo | using (var client = new HttpClient()) (causava esgotamento) |
IHttpClientFactory com políticas de resiliência |
| Serialização de JSON | JsonConvert.SerializeObject(obj) (Newtonsoft) |
JsonSerializer.Serialize(obj) (System.Text.Json) |
| Cache em memória | HttpRuntime.Cache.Insert(...) |
IMemoryCache ou HybridCache com tags |
| Logging estruturado | System.Diagnostics.Trace ou log4net |
ILogger e geradores de código [LoggerMessage] |
| Diagnóstico de saúde | Páginas .ashx rudimentares de ping |
app.MapHealthChecks("/healthz") com Liveness e Readiness |
| Exclusão em lote no banco | foreach (var item in itens) ctx.Clientes.Remove(item); |
await ctx.Clientes.Where(...).ExecuteDeleteAsync(); |
| Prevenção de CSRF | @Html.AntiForgeryToken() manual no formulário |
Automático com Tag Helpers do ASP.NET Core |
| Habilitação de CORS | Pacote Microsoft.AspNet.WebApi.Cors |
Nativo via builder.Services.AddCors() e app.UseCors() |
| Execução em contêiner | Impossível em Linux; imagens Windows de 8 GB | Contêineres Linux mínimos (Chiseled) abaixo de 45 MB |
| Compilação sem runtime | Inexistente (NGen dependia do CLR instalado) | Propriedade PublishAot=true gerando binário nativo de máquina |
Roteiro Prático de Migração: Da Teoria à Produção
Reescrever um sistema legado por completo raramente traz bons resultados. Projetos que interrompem entregas para reconstruir sistemas extensos costumam subestimar regras acumuladas ao longo dos anos, resultando em atrasos e desvios de escopo.
A migração gradual com isolamento de borda e transição incremental de rotas oferece um caminho controlado:
flowchart TD
Passo1["1. Isolamento das Bibliotecas de Negócio: Refatorar regras puras para .NET Standard 2.0 ou multi-target"]
Passo2["2. Fachada de Borda com Strangler Fig: Posicionar Gateway ou Proxy Reverso YARP na frente do legado"]
Passo3["3. Transição Incremental de Endpoints: Migrar rotas periféricas para Minimal APIs em .NET 10"]
Passo4["4. Descomissionamento Controlado: Desligar módulos do IIS conforme o tráfego atinge 100% no novo runtime"]
Passo1 --> Passo2
Passo2 --> Passo3
Passo3 --> Passo4
classDef step fill:#08202d,stroke:#3b82f6,stroke-width:1px,color:#f8fafc;
class Passo1,Passo2,Passo3,Passo4 step;
- Isolamento de Regras em .NET Standard 2.0: Classes de domínio que não tocam em
System.Webpodem ser convertidas primeiro para.NET Standard 2.0. Esse formato permite que a mesma biblioteca seja consumida tanto pelo monólito legado no .NET Framework 4.8.1 quanto pelos novos microsserviços em .NET 10. - Fachada com YARP (Yet Another Reverse Proxy): Posicionar um proxy reverso moderno na borda permite repassar inicialmente todo o tráfego ao IIS legado.
- Migração Rota a Rota com Anti-Corruption Layer (ACL): Novos módulos são construídos no .NET 10 e o proxy direciona rotas específicas para eles. Comunicações com tabelas antigas utilizam adaptadores de fronteira para evitar que convenções legadas poluam o novo modelo de dados.
- Desativação Progressiva do IIS: Conforme as rotas críticas são atendidas pelo novo runtime, o servidor Windows legado atrofia até que a infraestrutura clássica possa ser aposentada com segurança.
O Repositório de Testes no GitHub
Para inspecionar o código de cada um dos 36 conceitos com projetos compiláveis e prontos para execução, consulte o repositório prático da iniciativa:
- Repositório: github.com/muriloai/dotnet-modernization-lab
- Autor: Murilo de Abreu Inácio
- Estrutura: Cada diretório dentro de
concepts/abriga os subprojetosframework/enet10/, acompanhados de documentação técnica e instruções de execução.