EF Core 11: novidades e mudanças do .NET 11 Preview 7

O EF Core 11 recebe no .NET 11 Preview 7 mudanças concentradas em três frentes. São elas a tradução de consultas LINQ, a modernização do provider do Azure Cosmos DB e a ergonomia das ferramentas de migração.

Contexto do release do EF Core 11

As notas desta preview do EF Core 11 usam como referência a versão 11.0.0-preview.7.26381.103. Isso vale tanto para Microsoft.EntityFrameworkCore quanto para Microsoft.Data.Sqlite.Core.

O .NET 11 é uma versão de suporte padrão, classificada como STS. A disponibilidade geral está prevista para 10 de novembro de 2026, com suporte até 9 de novembro de 2028.

Por isso, o .NET 10, atual LTS, continua como linha de base para equipes restritas a versões de suporte estendido. Além disso, o EF Core 11 exige o SDK e o runtime do .NET 11 e não roda no .NET Framework.

Ainda assim, ignorar o EF Core 11 agora é uma decisão cara. O que amadurece nesta janela é o que a próxima LTS vai herdar, e adiar o contato só concentra o custo de migração.

Um ponto de leitura precisa ficar claro antes de seguir. Preview é software sujeito a alteração, portanto os comportamentos descritos aqui podem mudar até a versão final.

Para entender a evolução até aqui, vale ler o artigo sobre o EF Core no .NET 11 Preview 5.

Tradução de consultas LINQ no EF Core 11

A maior parte do esforço desta preview está no pipeline de tradução de consultas. O padrão que se repete no EF Core 11 é trocar subconsultas correlacionadas por junções.

Tradução de int.Parse no SQL Server

Os métodos byte.Parse, short.Parse, int.Parse, long.Parse, decimal.Parse, double.Parse e float.Parse agora viram CAST no SQL Server. A mudança vale para as sobrecargas simples que recebem apenas uma string.

Antes, essas chamadas caíam em avaliação no cliente. Com isso, o EF Core trazia dados adicionais para a memória antes de aplicar o filtro.

var rows = await context.BasicTypes
    .Where(o => int.Parse(Convert.ToString(o.Int)) == 12)
    .ToListAsync();

O SQL gerado agora resolve a conversão no servidor.

SELECT ...
FROM [BasicTypesEntities] AS [b]
WHERE CAST(CONVERT(nvarchar(max), [b].[Int]) AS int) = 12

Convém observar que a tradução resolve um problema de correção, e não de desempenho. Um CAST sobre coluna no WHERE continua atrapalhando o uso de índices, então filtre pelo tipo nativo sempre que possível.

Agregados de GroupBy sobre navegações no EF Core 11

Este é provavelmente o item de maior impacto prático da preview. Um agregado como g => g.Max(o => o.Customer.Region), que atravessa uma navegação de referência, gerava uma subconsulta correlacionada.

O efeito era severo, porque a subconsulta reexaminava a fonte de dados uma vez por grupo. Na prática, tratava-se de uma regressão introduzida no EF Core 7.

No EF Core 11, a expansão de navegações eleva a navegação para a fonte anterior ao GroupBy. Assim, o agregado volta ao formato de junção única seguida de agrupamento.

var byEmployee = await context.Orders
    .GroupBy(o => o.EmployeeId)
    .Select(g => new { EmployeeId = g.Key, TopRegion = g.Max(o => o.Customer.Region) })
    .ToListAsync();

O contraste entre o SQL antigo e o novo evidencia a diferença de custo.

-- Antes (EF Core 7 a 10): reexame correlacionado por grupo
SELECT (
    SELECT MAX([c].[Region])
    FROM [Orders] AS [o0]
    LEFT JOIN [Customers] AS [c] ON [o0].[CustomerId] = [c].[CustomerId]
    WHERE [o].[EmployeeId] = [o0].[EmployeeId]) AS [TopRegion], ...
FROM [Orders] AS [o]
GROUP BY [o].[EmployeeId]

-- Depois: LEFT JOIN único seguido de GROUP BY
SELECT [o].[EmployeeId], MAX([c].[Region]) AS [TopRegion]
FROM [Orders] AS [o]
LEFT JOIN [Customers] AS [c] ON [o].[CustomerId] = [c].[CustomerId]
GROUP BY [o].[EmployeeId]

Dois detalhes merecem atenção de quem revisa planos de execução. Primeiro, múltiplos agregados sobre a mesma navegação reutilizam uma única junção.

Além disso, navegações obrigatórias passam a usar INNER JOIN em vez do LEFT JOIN incondicional do GroupJoin anterior. Com isso, o otimizador descarta linhas mais cedo e considera novas estratégias de junção.

Composição sobre GroupBy seguido de First

Consultas que reduzem cada grupo a uma entidade com First, Single ou Last falhavam ao continuar a composição. O erro surgia ao projetar uma navegação, filtrar ou ordenar depois da redução.

A correção atua em duas camadas. Primeiro, a expansão de navegações guarda um instantâneo do seletor de elemento do grupo.

Em seguida, o vinculador de projeção relacional rebaixa subconsultas de resultado único sob demanda. Antes, isso só acontecia ao final da tradução.

var latestPerOrder = await context.TimeSheets
    .GroupBy(t => t.OrderId)
    .Select(g => g.OrderBy(x => x.Id).First())
    .OrderBy(t => t.Order.Number)
    .ToListAsync();

Esse padrão aparece com frequência em relatórios operacionais. Por exemplo, buscar o registro mais recente por chave e ordenar por um campo relacionado é comum em sistemas de auditoria.

Reuso de subconsulta no EF Core 11

Às vezes uma projeção lê vários membros da mesma subconsulta correlacionada de resultado único. Nesse caso, o EF Core 11 reescreve a projeção em um SelectMany, e os membros vêm de uma única junção.

As formas cobertas incluem First, Single, Last e ElementAt, além das variantes OrDefault e com predicado. Contudo, trata-se de uma solução intermediária enquanto o time reformula o mecanismo de seletores pendentes.

Acesso a membros de ValueTuple e Tuple

Acessar um item de um ValueTuple ou Tuple projetado antes na consulta falhava na tradução. Um caso concreto é uma chave de GroupBy do tipo ValueTuple<int, int> usada depois em uma junção.

A causa raiz é sutil. O compilador só popula NewExpression.Members para tipos anônimos, então o visitante de expressões ficava sem o mapeamento entre construtor e membros.

Agora o ReplacingExpressionVisitor resolve esses membros pelo nome do parâmetro do construtor. A cobertura inclui aridades de um a oito, inclusive a forma aninhada via Rest.

var joined = await context.Orders
    .GroupBy(o => new ValueTuple<int, int>(o.CustomerId, o.EmployeeId))
    .Join(
        context.Customers,
        g => g.Key.Item1,
        c => c.Id,
        (g, c) => new { c.Name, Count = g.Count() })
    .ToListAsync();

A restrição a esses dois tipos é deliberada. O mapeamento deles é um contrato fechado, enquanto classes e records arbitrários poderiam levar silenciosamente ao valor errado.

Projeções nulas em LEFT JOIN sem correspondência

Este item corrige uma exceção em um cenário legítimo. O problema surgia em um LEFT JOIN cujo lado interno projeta um tipo anônimo, um DTO ou um agregado de GroupBy.

Nas linhas sem correspondência, a consulta lançava InvalidOperationException. Na origem, o EF Core montava o objeto a partir de colunas nulas em vez de produzir null.

Entidades já tratavam esse caso pela verificação de nulidade nas colunas de chave. Agora a preview injeta uma coluna marcadora na subconsulta interna e condiciona a projeção a esse marcador.

var query = context.Owners
    .GroupJoin(
        context.Pets,
        o => o.Id,
        p => p.OwnerId,
        (o, ps) => new { o.Name, PetInfo = ps.Select(p => new { p.Name, p.Age }).FirstOrDefault() })
    // Antes lançava exceção para donos sem animais; agora PetInfo fica null.
    .ToListAsync();

O marcador também sobrevive a cenários compostos, como uma junção subsequente. Para structs, record structs e Nullable<T>, o resultado passa a ser default(T) em vez de exceção.

Provider do Azure Cosmos DB

O provider do Cosmos DB recebe uma das mudanças estruturais mais profundas desta versão. Por isso, quem opera cargas reais sobre ele deve ler esta seção com atenção.

Novo materializador do Cosmos DB no EF Core 11

No EF Core 11, o provider do Cosmos DB materializa resultados com Utf8JsonReader. Além disso, some a limitação histórica de Distinct em projeções no cliente.

A propriedade de apoio __jObject deixa de existir. Ao mesmo tempo, o pipeline troca o Newtonsoft.Json pelo System.Text.Json, via CosmosStructuralTypeSerializer.

Duas mudanças de comportamento exigem atenção em migração. A primeira: AsNoTrackingWithIdentityResolution passa a exigir as chaves primárias em projeções de tipos pertencidos.

A segunda envolve ponto flutuante materializado em tipos de ponto fixo, que agora sofre truncamento em vez de arredondamento. Por exemplo, 0.75 agora vira 0, como em (int)0.75 no .NET.

Quem dependia do arredondamento anterior precisa aplicá-lo de forma explícita, tipicamente com Math.Round.

var result = await context.Products
    // Arredondamento explícito para manter o comportamento antigo
    .Select(p => (int)Math.Round((double)p.Quantity / (p.Quantity + 1)))
    .SingleAsync();

Cabe um alerta para quem opera em produção. Sem o __jObject, propriedades JSON não mapeadas se perdem na ida e volta, e a documentação classifica isso como alto impacto.

Requisições passam pela estratégia de execução

Dois caminhos que ignoravam a política de repetição agora passam por ela. Primeiro, a verificação de status da resposta de consulta ocorre dentro da estratégia de execução.

Assim, falhas transitórias durante a iteração de páginas ganham nova tentativa. Antes, elas chegavam direto ao chamador.

Falhas em lotes transacionais também mudam. Agora elas surgem como exceções repetíveis, inclusive nos lotes por chave de partição do SaveChanges.

O ganho de confiabilidade é relevante. Afinal, sistemas que dependem de repetição contra limitação de taxa perdem dois pontos cegos.

Melhorias em migrações no EF Core 11

As ferramentas de migração do EF Core 11 recebem ajustes pequenos, porém úteis. Se o fluxo ainda é novo para você, revise antes o guia de migrations com EF Core e PostgreSQL.

NoBuild no Package Manager Console do EF Core 11

No EF Core 11, os cmdlets Add-Migration e Update-Database aceitam o parâmetro -NoBuild. Ele pula a compilação, assim como dotnet ef migrations add --no-build já fazia.

Add-Migration InitialCreate -NoBuild
Update-Database -NoBuild

Há dois cenários claros de uso. O primeiro é o projeto já compilado, em que a recompilação é apenas custo.

O segundo envolve avisos de vulnerabilidade do NuGet, como NU1902, que bloqueariam o comando. Contudo, use o parâmetro só com o build atualizado.

Migrations bundle com Central Package Management

O comando dotnet ef migrations bundle falhava com NU1008 em soluções com gerenciamento centralizado de pacotes. A causa era uma PackageReference versionada que o projeto temporário sempre emitia.

Agora o projeto de bundle emite referências condicionais. Ou seja, sem CPM a referência leva versão, e com CPM ela usa o PackageVersion do Directory.Packages.props.

Caminhos de deps e runtimeconfig

A ferramenta dotnet ef montava os caminhos de --depsfile e --runtimeconfig a partir de AssemblyName. Isso quebrava projetos que combinam TargetName com ProjectDepsFileName.

Agora a ferramenta lê as propriedades MSBuild ProjectDepsFileName e ProjectRuntimeConfigFileName. Quando elas estão vazias, volta ao comportamento anterior.

Reordenação em transições de sequência TPC

Hierarquias TPC que consolidavam sequências em uma sequência compartilhada geravam migrações inválidas. O mesmo acontecia ao renomear essa sequência compartilhada.

O motivo era de ordenação, porque a sequência antiga sumia antes da reescrita dos valores padrão. A correção reorganiza essas operações.

Suporte ao tipo Half no SQLite com EF Core 11

Com o EF Core 11, o Microsoft.Data.Sqlite e o provider SQLite passam a ler, vincular e mapear System.Half. Esse é o tipo de ponto flutuante de 16 bits do .NET 5, e o suporte inclui colunas JSON.

public class Sensor
{
    public int Id { get; set; }

    // Precisão de 16 bits, suficiente para muitas leituras de sensor
    public Half Temperature { get; set; }
}

O caso de uso mais evidente é telemetria embarcada. Cenários de IoT com SQLite local economizam espaço quando 16 bits bastam.

Por outro lado, o tipo continua inadequado para valores monetários ou cálculos que exigem precisão.

Mudança incompatível no Microsoft.Data.Sqlite

Os pacotes Microsoft.Data.Sqlite e Microsoft.Data.Sqlite.Core deixam de mirar netstandard2.0. Agora eles miram apenas net10.0, o TFM mínimo suportado.

Na prática, o .NET Framework perde o suporte ao Microsoft.Data.Sqlite a partir da versão 11. A justificativa é que netstandard2.0 sugeria suporte a runtimes antigos e escondia diferenças de API.

Um exemplo dessas diferenças é a ausência de DateOnly e TimeOnly nos alvos antigos. Aplicações que já miram net10.0 ou superior não sentem a mudança.

Já quem precisa permanecer no .NET Framework deve ficar na linha de serviço 10.0.x mais recente.

Correções de bugs relevantes

O volume de correções desta preview é expressivo. Por isso, vale destacar as categorias com maior impacto em produção.

Na tradução de consultas, o time corrigiu a rejeição de entidades JSON alcançadas só por Include sob AsNoTrackingWithIdentityResolution. Também melhorou o tratamento de nulos em coleções primitivas mapeadas para JSON.

Ainda nessa área, o EF Core 11 corrige nomes inválidos de propriedades sombra em consultas pré compiladas. Além disso, resolve linhas fora de ordem em Include dividido durante inserções concorrentes.

No rastreamento de mudanças, a preview corrige um conflito de identidade em entidades pertencidas com dados inconsistentes. Também elimina uma exceção indevida do DetectChanges com propriedades complexas aninhadas anuláveis.

Duas correções afetam a fidelidade do esquema. Uma trata de chave estrangeira apontando para a tabela errada com divisão de entidade, e a outra trata de tabelas de mesmo nome em esquemas distintos.

Leitura crítica do EF Core 11 Preview 7

Há uma linha condutora clara no EF Core 11 Preview 7: trocar subconsultas correlacionadas por junções. De fato, três dos seis itens de tradução LINQ seguem esse padrão.

Isso indica maturidade do pipeline de consulta. As mudanças não ampliam a API, mas corrigem formas de SQL que degradavam com o volume de dados.

Um segundo padrão é trocar exceções por comportamento correto. A materialização de null e a reordenação de migrações TPC eliminam contornos que o ORM obrigava.

A recomendação prática é direta. Equipes com GroupBy sobre navegações devem testar o EF Core 11 em ambiente controlado e comparar planos de execução.

Para produção, a orientação permanece conservadora. Preview não é destino de implantação, e a adoção do .NET 11 depende da tolerância ao ciclo de dois anos.

Para ver o restante desta preview, confira as novidades do runtime do .NET 11 Preview 7. A lista completa está na documentação oficial do que há de novo.

Conclusão

O EF Core 11 no .NET 11 Preview 7 não é um release de funcionalidades vistosas. Na verdade, ele corrige a estrutura do pipeline de tradução, com efeitos mensuráveis em planos de execução.

A contribuição da comunidade chama atenção neste ciclo. Itens como a elevação de agregados de GroupBy e o suporte a Half vieram de colaboradores externos.

Para quem tem o EF Core 11 no horizonte, dois pontos exigem planejamento: o provider do Cosmos DB e a saída do netstandard2.0. Já as melhorias de tradução exigem apenas validação.

FAQ

O .NET 11 Preview 7 pode ser usado em produção?

Não. Versões preview não têm suporte para produção e podem mudar até a disponibilidade geral, prevista para novembro de 2026.

A elevação de agregados de GroupBy exige alteração no código?

Não. A mudança ocorre no pipeline de tradução, e as consultas existentes passam a gerar SQL diferente sem reescrita.

Por que navegações obrigatórias passaram a usar INNER JOIN?

Porque o GroupJoin anterior produzia LEFT JOIN sempre. O INNER JOIN permite ao otimizador descartar linhas mais cedo.

Minha aplicação .NET Framework usa Microsoft.Data.Sqlite. O que fazer?

O ideal é migrar para .NET 10 ou superior. Se isso não for viável, permaneça na linha 10.0.x mais recente do pacote.

O truncamento no Cosmos DB afeta dados já gravados?

Não afeta o dado armazenado, apenas o valor lido em tipos de ponto fixo. Use Math.Round se precisar do comportamento anterior.

O parâmetro -NoBuild é seguro em pipelines de CI?

Sim, quando o projeto já foi compilado na etapa anterior. Sobre código desatualizado, ele usa binários antigos.

Vale a pena adotar Half no SQLite?

Vale quando 16 bits de precisão bastam, como em telemetria. Para valores monetários, o tipo é inadequado.

Glossário

Termo Significado
ORM Object Relational Mapper, camada que mapeia objetos de domínio para estruturas relacionais
EF Core Entity Framework Core, o ORM oficial da plataforma .NET
STS Standard Term Support, ciclo de suporte padrão de dois anos
LTS Long Term Support, ciclo de suporte estendido de três anos
TFM Target Framework Moniker, identificador do alvo de compilação de um projeto
Subconsulta correlacionada Subconsulta cujo resultado depende de valores da consulta externa
Expansão de navegações Etapa do pipeline do EF Core que converte acessos a navegações em junções ou subconsultas
Materialização Conversão das linhas retornadas pelo banco em objetos CLR
Propriedade sombra Propriedade existente apenas no modelo do EF Core, sem correspondente na classe
Tipo pertencido Tipo sem identidade própria que existe apenas no contexto da entidade proprietária
TPC Table Per Concrete Type, estratégia de herança com uma tabela por tipo concreto
CPM Central Package Management, recurso do NuGet que centraliza versões de pacotes
PMC Package Manager Console, console de pacotes do Visual Studio
Estratégia de execução Componente do EF Core responsável por políticas de repetição diante de falhas transitórias
Half Tipo de ponto flutuante de 16 bits do .NET, presente desde o .NET 5
Utf8JsonReader Leitor JSON de baixa alocação do System.Text.Json
DTO Data Transfer Object, objeto usado para transportar dados entre camadas
Aridade Número de parâmetros de tipo de uma estrutura genérica

Fontes