.NET 11 e C# 15: Recursos e Otimizações no EF Core e LINQ
   Hoje teremos uma análise das novidades do .NET 11 Preview 5 e C# 15, com exemplos práticos do mundo real para Union Types, Closed Class Hierarchies, Full Outer Join no LINQ e diagnósticos do EF Core.

A Microsoft disponibilizou a algumas semanas atrás o .NET 11 Preview 5 (na verdade já estamos na preview 7). Para evitar a leitura maçante de notas de lançamento genéricas no GitHub, compilei aqui os recursos essenciais do C# 15 e do ecossistema .NET com explicações e exemplos voltados para o dia a dia do desenvolvimento.

Por se tratar de uma versão de pré-visualização (Preview), estes recursos não devem ser aplicados em ambientes de produção até o lançamento oficial previsto para novembro. No entanto, é fundamental compreender como essas mudanças impactam a modelagem do seu código.

 

1. C# 15: Tipos Union (Discriminated Unions)

Uma das adições mais solicitadas pelos desenvolvedores C# é o suporte nativo a declarações de union. Inspirado em linguagens como F# e TypeScript, o recurso permite definir um tipo que pode assumir estritamente um conjunto delimitado de outros tipos.

Diferente de abordagens baseadas em herança comum ou interfaces genéricas, o compilador possui conhecimento exato de todos os tipos possíveis da união, tornando a expressão switch totalmente exaustiva sem a necessidade de uma cláusula default:

public record class Cachorro(string Nome);
public record class Gato(int Vidas);

// Declaração do tipo Union
public union Pet(Cachorro, Gato);

// O compilador valida se todos os tipos da Union foram tratados
static string Descrever(Pet pet) => pet switch
{
    Cachorro(string nome) => $"Cachorro: {nome}",
    Gato(int vidas)       => $"Gato com {vidas} vidas"
};

Se você adicionar um novo tipo ao Pet futuramente e não atualizar o switch, o compilador emitirá um aviso de compilação (*warning*), garantindo maior segurança em tempo de desenvolvimento.

2. Closed Class Hierarchies (Hierarquias de Classe Fechadas)

Complementando os tipos *union*, o C# 15 introduz o modificador closed para classes base. Ao marcar uma classe como closed, você restringe a herança direta apenas a tipos declarados dentro do mesmo *assembly*.

Isso é extremamente útil para modelar resultados de domínio fortemente tipados (como retornos de APIs ou processamento de negócios), garantindo que nenhuma biblioteca externa possa derivar novas subclasses não mapeadas:

// Classe base fechada para herança fora deste assembly
public closed record class ResultadoPagamento;

// Subclasses explicitamente permitidas dentro do projeto
public record class Sucesso(string TransacaoId) : ResultadoPagamento;
public record class Falha(string Motivo) : ResultadoPagamento;
public record class Pendente(DateTime Previsao) : ResultadoPagamento;

// Garantia de verificação exaustiva sem necessidade de default
static string Processar(ResultadoPagamento resultado) => resultado switch
{
    Sucesso(var id)    => $"Aprovado! Transação: {id}",
    Falha(var motivo)  => $"Recusado: {motivo}",
    Pendente(var data) => $"Aguardando até {data:dd/MM/yyyy}"
};

A grande vantagem frente ao padrão tradicional com abstract
e sealed é que o próprio compilador consegue provar que a hierarquia está selada, impondo rigor nos comandos switch.

3. LINQ: Suporte Nativo a Full Outer Joins

Enquanto o SQL suporta o comando FULL OUTER JOIN há décadas, as consultas LINQ em memória exigiam combinatórias complexas com GroupJoin e SelectMany. O .NET 11 simplifica isso introduzindo o método FullJoin diretamente no LINQ:

Produto[] catalogo =
[
    new("A100", "Teclado"),
    new("B200", "Mouse"),
];

Venda[] vendas =
[
    new("B200", 4),
    new("C300", 2),
];

IEnumerable<object> linhas = catalogo.FullJoin(
    vendas,
    produto => produto.Sku,
    venda => venda.Sku,
    (produto, venda) => new
    {
        Sku = produto?.Sku ?? venda!.Sku,
        Nome = produto?.Nome ?? "(fora do catálogo)",
        Vendidos = venda?.Quantidade ?? 0,
    });

O resultado incluirá dados que coincidem em ambos os lados (Mouse B200), dados sem correspondência no lado esquerdo (Teclado A100) e vendas de itens não cadastrados no catálogo (C300). No EF Core, esse método também se estende à interface IQueryable, traduzindo a consulta diretamente para o banco de dados.

4. EF Core: Análise de Chamadas Síncronas (EF1004) e SQL Otimizado

No Entity Framework Core, o Preview 5 traz duas importantes melhorias operacionais:

- Alerta EF1004: O compilador passa a emitir um aviso ao identificar chamadas síncronas em métodos assíncronos (como o uso de .Result
ou .GetAwaiter().GetResult() em consultas do EF). Essa prática é uma das maiores causadoras de *starvation* de threads e *deadlocks* sob carga em aplicações ASP.NET Core.

- Geração de SQL mais limpo: A engine de tradução foi ajustada para remover subconsultas redundantes e junções (*joins*) desnecessárias, gerando um código SQL mais próximo do escrito manualmente.

5. Serialização de JSON Lines (NDJSON) e APIs Genéricas de Random

O formato JSON Lines (onde cada linha representa um objeto JSON independente) é amplamente adotado em *pipeline* de dados e *streams* de logs. O namespace System.Text.Json agora suporta essa formatação nativamente via parâmetro topLevelValues: true:

await JsonSerializer.SerializeAsyncEnumerable(
    stream,
    ObterLeituras(),
    topLevelValues: true);

Na classe Random.Shared, foram adicionadas sobrecargas genéricas como NextInteger<T>() e NextBinaryFloat<T>(), eliminando a necessidade de converter ou checar tipos manualmente ao escrever algoritmos genéricos.

6. Atualizações no SDK e Blazor

- MCP Server Template: O CLI do .NET traz o modelo `dotnet new mcp` para facilitar a criação de servidores baseados no *Model Context Protocol*, facilitando a integração de aplicações .NET com agentes de Inteligência Artificial.

- Auditoria de Vulnerabilidades no Build: O processo de compilação passa a alertar automaticamente se algum pacote NuGet do projeto possui CVEs conhecidas ou atingiu o fim de suporte (*EOL*).

- Blazor SSR: Suporte a validação no lado do cliente sem exigir modos interativos completos, compatibilidade do componente `QuickGrid` em páginas estáticas e o atributo [SupplyParameterFromSession] para vincular dados da sessão aos componentes.

Conclusão:

As atualizações do .NET 11 e C# 15 demonstram uma clara evolução voltada para a robustez na modelagem de código e redução de falhas em tempo de execução. Vale a pena criar projetos de teste para explorar os recursos de *Union Types* e *Closed Hierarchies*, pois eles alterarão positivamente a forma como estruturamos o domínio de nossas aplicações.

E estamos conversados..

"Para que possais andar dignamente diante do Senhor, agradando-lhe em tudo, frutificando em toda a boa obra, e crescendo no conhecimento de Deus;"
Colossenses 1:10
 

Referências:


José Carlos Macoratti