Mudanças significativas no EF Core 9 (EF9)

Esta página documenta as alterações de API e comportamento que têm o potencial de interromper a atualização de aplicativos existentes do EF Core 8 para o EF Core 9. Certifique-se de revisar as alterações importantes anteriores ao atualizar de uma versão anterior do EF Core.

Estrutura de Destino

O EF Core 9 tem como alvo .NET 8. Isto significa que as aplicações existentes que visam o .NET 8 podem continuar a fazê-lo. Aplicações direcionadas a versões antigas de .NET, .NET Core e .NET Framework terão de apontar para .NET 8 ou .NET 9 para usar EF Core 9.

Resumo

Observação

Se estiver a usar Azure Cosmos DB, por gentileza consulte a secção indicado abaixo sobre alterações de quebra do Azure Cosmos DB.

Mudança disruptiva Impacto
exceção é lançada ao aplicar migrações se houver alterações de modelo pendentes Alto
Exceção é lançada ao aplicar migrações numa transação explícita Alto
Microsoft.EntityFrameworkCore.Design não encontrado ao usar ferramentas do EF Média
EF.Functions.Unhex() agora retorna byte[]? Baixo
Os modelos compilados agora referenciam diretamente métodos de conversor de valor Baixo
A aridade dos argumentos de nulidade de SqlFunctionExpression foi validada Baixo
ToString() método agora retorna sequência vazia para null instâncias Baixo
As dependências da estrutura compartilhada foram atualizadas para 9.0.x Baixo
As ferramentas EF já não suportam projetos .NET Framework Baixo
EF.Constant() e EF.Parameter() já não funcionam dentro de consultas compiladas Baixo

Alterações de alto impacto

É lançada uma exceção ao aplicar migrações se existirem mudanças no modelo pendentes

Problema de rastreamento #33732

Comportamento antigo

Se o modelo tiver alterações pendentes em comparação com a última migração, elas não serão aplicadas com o restante das migrações quando Migrate for chamada.

Novo comportamento

A partir do EF Core 9.0, se o modelo tiver alterações pendentes em comparação com a última migração, será lançada uma exceção quando dotnet ef database update, Migrate ou MigrateAsync forem chamados:

O modelo de contexto 'DbContext' tem alterações pendentes. Adicione uma nova migração antes de atualizar o banco de dados. Essa exceção pode ser suprimida ou registrada passando a ID do evento 'RelationalEventId.PendingModelChangesWarning' para o método 'ConfigureWarnings' em 'DbContext.OnConfiguring' ou 'AddDbContext'.

Porquê

Esquecer de adicionar uma nova migração depois de fazer alterações no modelo é um erro comum que pode ser difícil de diagnosticar em alguns casos. A nova exceção garante que o modelo do aplicativo corresponda ao banco de dados após a aplicação das migrações.

Atenuações

Existem várias situações comuns em que esta exceção pode ser lançada:

  • Não há migrações. Isso é comum quando o banco de dados é atualizado por outros meios.

    • de mitigação: se você não planeja usar migrações para gerenciar o esquema de banco de dados, remova a chamada Migrate ou MigrateAsync, caso contrário, adicione uma migração.
  • Há pelo menos uma migração, mas a captura do modelo está em falta. Isso é comum para migrações criadas manualmente.

    • de mitigação: adicione uma nova migração usando ferramentas do EF, isso atualizará a captura de modelo.
  • O modelo não foi modificado pelo desenvolvedor, mas é construído de forma não determinística, fazendo com que o EF o detete como modificado. Isso é comum quando new DateTime(), DateTime.Now, DateTime.UtcNowou Guid.NewGuid() são usados em objetos fornecidos a HasData.

    • Mitigação: Adicione uma nova migração, examine seu conteúdo para localizar a causa e substitua os dados dinâmicos por um valor estático e codificado no modelo. A migração deve ser recriada após a correção do modelo. Se os dados dinâmicos tiverem que ser usados para semeadura, considere usar o novo padrão de semeadura em vez de HasData.
  • A última migração foi criada para um provedor diferente daquele usado para aplicar as migrações.

  • As migrações são geradas, modificadas ou escolhidas dinamicamente substituindo alguns dos serviços EF.

    • Mitigação: O aviso é um falso positivo neste caso e deve ser suprimido:
      options.ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning))
  • Está a usar o ASP.NET Core Identity e as opções de alteração que afetam o modelo, tais como:

    .AddDefaultIdentity<ApplicationUser>(options =>
        {
            options.Stores.SchemaVersion = IdentitySchemaVersions.Version2;
            options.Stores.MaxLengthForKeys = 256;
            options.SignIn.RequireConfirmedAccount = false;
        })
    
    • Atenuação: Para garantir que as opções sejam aplicadas de forma consistente, o aplicativo precisa ser especificado como o projeto de inicialização ao executar as ferramentas do EF ou, alternativamente, IDesignTimeDbContextFactory precisa ser implementado no projeto que contém o DbContext:

      public class DatabaseContextDesignTimeFactory : IDesignTimeDbContextFactory<DatabaseContext>
      {
          public DatabaseContext CreateDbContext(string[] args)
          {
              var services = new ServiceCollection();
              AddIdentity(services);
              var serviceProvider = services.BuildServiceProvider();
              var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
              optionsBuilder.UseApplicationServiceProvider(serviceProvider);
              optionsBuilder.UseSqlServer();
              return new DatabaseContext(optionsBuilder.Options);
          }
      
          public static IServiceCollection AddIdentity(IServiceCollection services)
          {
              services.AddDefaultIdentity<ApplicationUser>(options =>
                  {
                      options.Stores.SchemaVersion = IdentitySchemaVersions.Version2;
                      options.Stores.MaxLengthForKeys = 256;
                      options.SignIn.RequireConfirmedAccount = false;
                  })
                  .AddRoles<IdentityRole>()
                  .AddEntityFrameworkStores<DatabaseContext>();
      
              return services;
          }
      }
      

Se o seu cenário não se enquadrar em nenhum dos casos mencionados acima e adicionar uma nova migração resultar na criação da mesma migração cada vez ou numa migração vazia, e a exceção ainda for lançada, então crie um pequeno projeto de reprodução e partilhá-lo com a equipa do EF num novo assunto.

Uma exceção é lançada ao aplicar migrações numa transação explícita.

Problema de rastreamento #17578

Comportamento antigo

Para aplicar migrações de forma resiliente, o seguinte padrão foi comumente usado:

await dbContext.Database.CreateExecutionStrategy().ExecuteAsync(async () =>
{
    await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
    await dbContext.Database.MigrateAsync(cancellationToken);
    await transaction.CommitAsync(cancellationToken);
});

Novo comportamento

A partir do EF Core 9.0, as chamadas Migrate e MigrateAsync iniciarão uma transação e executarão os comandos usando um ExecutionStrategy e, se seu aplicativo usar o padrão acima, uma exceção será lançada:

Gerou-se um erro relativo ao aviso 'Microsoft.EntityFrameworkCore.Migrations.MigrationsUserTransactionWarning': Foi iniciada uma transação antes da aplicação das migrações. Isso impede que um bloqueio de banco de dados seja adquirido e, portanto, o banco de dados não será protegido contra aplicativos de migração simultânea. As transações e a estratégia de execução já são gerenciadas pela EF conforme necessário. Remova a transação externa. Essa exceção pode ser suprimida ou registrada passando a ID do evento 'RelationalEventId.MigrationsUserTransactionWarning' para o método 'ConfigureWarnings' em 'DbContext.OnConfiguring' ou 'AddDbContext'.

Porquê

O uso de uma transação explícita impede que um bloqueio de banco de dados seja adquirido e, portanto, o banco de dados não será protegido contra aplicativos de migração simultânea, também limita o EF sobre como ele pode gerenciar as transações internamente.

Atenuações

Se houver apenas uma chamada de banco de dados dentro da transação, remova a transação externa e ExecutionStrategy:

await dbContext.Database.MigrateAsync(cancellationToken);

Caso contrário, se o seu cenário exigir uma transação explícita e você tiver outro mecanismo em vigor para impedir o aplicativo de migração simultânea, ignore o aviso:

options.ConfigureWarnings(w => w.Ignore(RelationalEventId.MigrationsUserTransactionWarning))

Alterações de impacto médio

Microsoft.EntityFrameworkCore.Design não é encontrado ao usar ferramentas do EF

Problema de rastreamento #35265

Comportamento antigo

Anteriormente, as ferramentas EF exigiam que Microsoft.EntityFrameworkCore.Design fossem referenciadas da seguinte forma.

    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="*.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>

Novo comportamento

A partir do .NET SDK 9.0.200, é lançada uma exceção quando uma ferramenta EF é invocada:

Não foi possível carregar o ficheiro ou assembly 'Microsoft.EntityFrameworkCore.Design, Culture=neutral, PublicKeyToken=null'. O sistema não consegue encontrar o ficheiro especificado.

Porquê

As ferramentas EF baseavam-se num comportamento não documentado do SDK .NET que fazia com que ativos privados fossem incluídos no ficheiro gerado .deps.json. Isso foi corrigido em sdk#45259. Infelizmente, a alteração do EF para levar isso em consideração não atende ao critério de manutenção do EF 9.0.x, portanto, será resolvido no EF 10.

Atenuações

Como solução alternativa antes do lançamento do EF 10, você pode marcar a referência de assembly Design como publicável:

    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <Publish>true</Publish>
    </PackageReference>

Isto incluirá-lo-á no ficheiro gerado .deps.json, mas tem o efeito secundário de copiar Microsoft.EntityFrameworkCore.Design.dll para as pastas de saída e publicação.

Alterações de baixo impacto

EF.Functions.Unhex() agora retorna byte[]?

Problema de rastreamento #33864

Comportamento antigo

A função EF.Functions.Unhex() foi previamente anotada para retornar byte[].

Novo comportamento

A partir do EF Core 9.0, o Unhex() agora é anotado para retornar byte[]?.

Porquê

Unhex() é traduzido para a função SQLite unhex, que retorna NULL para entradas inválidas. Como resultado, Unhex() devolveu null para esses casos, violando a anotação.

Atenuações

Se você tiver certeza de que o conteúdo de texto passado para Unhex() representa uma cadeia de caracteres hexadecimal válida, você pode simplesmente adicionar o operador de perdão nulo como uma asserção de que a invocação nunca retornará null:

var binaryData = await context.Blogs.Select(b => EF.Functions.Unhex(b.HexString)!).ToListAsync();

Caso contrário, adicione verificações de tempo de execução para "null" no valor de retorno de Unhex().

Os modelos compilados agora referenciam diretamente métodos de conversor de valor

Problema de Rastreio #35033

Comportamento antigo

Anteriormente, ao usar conversores de valor com modelos compilados (usando dotnet ef dbcontext optimize), o EF referenciava o tipo de conversor e tudo funcionava corretamente.

public sealed class BooleanToCharConverter() : ValueConverter<bool, char>(v => ConvertToChar(v), v => ConvertToBoolean(v))
{
    public static readonly BooleanToCharConverter Default = new();

    private static char ConvertToChar(bool value) // Private method
        => value ? 'Y' : 'N';

    private static bool ConvertToBoolean(char value) // Private method
        => value == 'Y';
}

Novo comportamento

A partir do EF Core 9.0, o EF gera código que faz referência direta aos próprios métodos de conversão. Se estes métodos forem privados, a compilação falhará.

Porquê

Esta alteração foi necessária para suportar o NativeAOT.

Atenuações

Torne os métodos referenciados pelos conversores de valor públicos ou internos em vez de privados.

Aridade dos argumentos de anulabilidade da SqlFunctionExpression validada

Problema de Acompanhamento #33852

Comportamento antigo

Anteriormente, era possível criar um SqlFunctionExpression com um número diferente de argumentos e argumentos de propagação de anulabilidade.

Novo comportamento

Começando com o EF Core 9.0, o EF agora lança se o número de argumentos e os argumentos de propagação de anulabilidade não corresponderem.

Porquê

Não ter um número correspondente de argumentos e argumentos de propagação de anulabilidade pode levar a um comportamento inesperado.

Atenuações

Certifique-se de que o argumentsPropagateNullability tem o mesmo número de elementos que o arguments. Em caso de dúvida, use false para o argumento de anulabilidade.

O método ToString() agora retorna uma string vazia para instâncias null

Problema de rastreamento #33941

Comportamento antigo

Anteriormente, o EF retornava resultados inconsistentes para o método ToString() quando o valor do argumento era null. Por exemplo, ToString() na propriedade bool? com o valor null que retornou null, mas para expressões de não-propriedade bool? cujo valor foi null, retornou True. O comportamento também foi inconsistente para outros tipos de dados, por exemplo, ToString() em null valor enum retornou string vazia.

Novo comportamento

A partir do EF Core 9.0, o método ToString() agora retorna consistentemente a cadeia de caracteres vazia em todos os casos em que o valor do argumento é null.

Porquê

O comportamento antigo era inconsistente em diferentes tipos de dados e situações, além de não estar alinhado com o comportamento de C#.

Atenuações

Para reverter para o comportamento antigo, reescreva a consulta de acordo:

var newBehavior = context.Entity.Select(x => x.NullableBool.ToString());
var oldBehavior = context.Entity.Select(x => x.NullableBool == null ? null : x.NullableBool.ToString());

As dependências da estrutura compartilhada foram atualizadas para 9.0.x

Comportamento antigo

Aplicações que usavam o SDK Microsoft.NET.Sdk.Web e visavam net8.0 resolviam pacotes como System.Text.Json, Microsoft.Extensions.Caching.Memory, Microsoft.Extensions.Configuration.Abstractions, Microsoft.Extensions.Logging e Microsoft.Extensions.DependencyModel a partir do framework partilhado, por isso estes assemblies normalmente não seriam implementados com a app.

Novo comportamento

Embora o EF Core 9.0 ainda suporte net8.0, agora faz referência às versões 9.0.x de System.Text.Json, Microsoft.Extensions.Caching.Memory, Microsoft.Extensions.Configuration.Abstractions, Microsoft.Extensions.Logging e Microsoft.Extensions.DependencyModel. As aplicações destinadas ao net8.0 não poderão utilizar a estrutura partilhada para evitar a implantação destes conjuntos.

Porquê

As versões de dependência correspondentes contêm as correções de segurança mais recentes e usá-las simplifica o modelo de manutenção para o EF Core.

Atenuações

Altere seu aplicativo para o destino net9.0 para obter o comportamento anterior.

As ferramentas EF já não suportam projetos .NET Framework

Problema de Rastreamento #37745

Comportamento antigo

Anteriormente, as ferramentas EF Core (ferramentas de CLI e do Console do Gerenciador de Pacotes) trabalhavam com projetos direcionados para o .NET Framework.

Novo comportamento

A partir do EF Core 9.0, as ferramentas EF Core deixaram de funcionar com projetos direcionados ao .NET Framework. As ferramentas produzem um erro quando o projeto inicial tem como alvo o .NET Framework.

Porquê

A versão atual das ferramentas EF Core funciona com todas as versões suportadas do EF Core e já não existem versões suportadas do EF Core que funcionem no .NET Framework.

Atenuações

Atualize o seu projeto para o .NET alvo (por exemplo, .NET 8 ou posterior). Se o seu projeto tem atualmente como objetivo .NET Framework, consulte o guia porting para informações sobre migração para .NET.

EF.Constant() e EF.Parameter() já não funcionam dentro de consultas compiladas

Problema de Rastreamento #33674

Comportamento antigo

No EF Core 8, Constant e Parameter podia ser usado dentro de consultas compiladas (CompileQuery e CompileAsyncQuery):

var lookbackDays = 7;

var compiledQuery = EF.CompileAsyncQuery(
    (AppDbContext db) => db.Blogs
        .Where(b => b.PublishedOn >= DateTime.Today.AddDays(EF.Constant(-lookbackDays))));

Novo comportamento

A partir do EF Core 9.0, usando Constant ou Parameter dentro de uma consulta compilada, lança um InvalidCastException:

Unable to cast object of type 'System.Linq.Expressions.ConstantExpression' to type 'System.Linq.Expressions.ParameterExpression'.

Porquê

A implementação interna de Constant foi alterada para evitar a recompilação completa das consultas para cada valor constante diferente. A implementação anterior introduziu nós constantes cedo no fluxo de consulta (antes do armazenamento em cache das consultas), resultando em misses de cache caros sempre que um valor diferente era passado para EF.Constant(). A nova implementação processa estes métodos numa fase posterior, o que é incompatível com consultas compiladas.

Atenuações

Ou remova a chamada Constant ou Parameter da consulta compilada, ou deixe de usar uma consulta compilada para essa consulta em particular. Note-se que remover EF.Constant() faz com que o valor seja enviado como um parâmetro SQL em vez de ser inlineado como uma constante, o que pode afetar o desempenho do plano de consulta.

Alterações significativas no Azure Cosmos DB

Foi feito um trabalho extenso para melhorar o fornecedor Azure Cosmos DB na versão 9.0. As mudanças incluem uma série de alterações de impacto significativo; Se estiver a atualizar uma aplicação existente, leia cuidadosamente o que se segue.

Mudança disruptiva Impacto
A propriedade discriminador agora é chamada $type em vez de Discriminator Alto
A propriedade id não contém mais o discriminador por padrão Alto
A propriedade JSON id é mapeada para a chave Alto
Sync I/O através do fornecedor Azure Cosmos DB já não é suportado Média
consultas SQL agora devem projetar valores JSON diretamente Média
Os resultados indefinidos agora são filtrados automaticamente dos resultados da consulta Média
As consultas traduzidas incorretamente não são mais traduzidas Média
HasIndex agora lança em vez de ser ignorado Baixo
IncludeRootDiscriminatorInJsonId foi renomeado para HasRootDiscriminatorInJsonId após 9.0.0-rc.2 Baixo
A versão referenciada do Newtonsoft.Json foi atualizada da 10.0.2 para a 13.0.1 Baixo

Alterações de alto impacto

A propriedade discriminator agora chama-se $type em vez de Discriminator

Problema de rastreamento #34269

Comportamento antigo

O EF adiciona automaticamente uma propriedade discriminadora aos documentos JSON para identificar o tipo de entidade que o documento representa. Em versões anteriores do EF, essa propriedade JSON costumava ser nomeada Discriminator por padrão.

Novo comportamento

A partir do EF Core 9.0, a propriedade discriminator agora é chamada de $type por padrão. Se tiver documentos existentes em Azure Cosmos DB de versões anteriores do EF, estes usam a antiga nomeação Discriminator e, após atualizar para o EF 9.0, as consultas a esses documentos falharão.

Porquê

Uma prática JSON emergente usa uma propriedade $type em cenários onde o tipo de um documento precisa ser identificado. Por exemplo, o System.Text.Json da .NET também suporta polimorfismo, usando $type como nome padrão da propriedade discriminadora (docs). Para alinhar com o resto do ecossistema e facilitar a interoperação com ferramentas externas, o padrão foi alterado.

Atenuações

A mitigação mais simples é simplesmente configurar o nome da propriedade discriminadora como Discriminator, tal como antes, usando HasDiscriminator:

modelBuilder.Entity<Session>().HasDiscriminator<string>("Discriminator");

Fazer isso para todos os tipos de entidade de nível superior fará com que o EF se comporte exatamente como antes.

Neste ponto, se desejar, você também pode atualizar todos os seus documentos para usar a nova nomenclatura $type.

A propriedade id agora contém apenas a propriedade de chave EF por predefinição

Problema de rastreamento #34179

Comportamento antigo

Anteriormente, o EF inseria o valor discriminador do seu tipo de entidade na propriedade id do documento. Por exemplo, se você salvou um tipo de entidade Blog com uma propriedade Id contendo 8, a propriedade JSON id conterá Blog|8.

Novo comportamento

A partir do EF Core 9.0, a propriedade JSON id não contém mais o valor do discriminador e contém apenas o valor da propriedade da chave. Para o exemplo acima, a propriedade JSON id seria simplesmente 8. Se tiver documentos existentes em Azure Cosmos DB de versões anteriores do EF, estes têm o valor discriminador na propriedade JSON id e, após atualizar para o EF 9.0, as consultas a esses documentos falharão.

Porquê

Como a propriedade JSON id deve ser exclusiva, o discriminador foi adicionado anteriormente a ela para permitir que entidades diferentes com o mesmo valor de chave existam. Por exemplo, isso permitia ter um Blog e um Post com uma propriedade Id contendo o valor 8 dentro do mesmo contêiner e partição. Isso se alinhou melhor com os padrões de modelagem de dados de banco de dados relacional, onde cada tipo de entidade é mapeado para sua própria tabela e, portanto, tem seu próprio espaço-chave.

O EF 9.0 alterou geralmente o mapeamento para estar mais alinhado com as práticas e expectativas de Azure Cosmos DB NoSQL comuns, em vez de corresponder às expectativas dos utilizadores provenientes de bases de dados relacionais. Além disso, ter o valor discriminador na propriedade id dificultava a interação de ferramentas e sistemas externos com documentos JSON gerados por EF; estes sistemas externos geralmente não estão conscientes dos valores discriminadores EF, que por defeito são derivados dos tipos .NET.

Atenuações

A mitigação mais fácil é simplesmente configurar o EF para incluir o discriminador na propriedade JSON id , como antes de usar HasDiscriminatorInJsonId. Uma nova opção de configuração foi introduzida para este fim:

modelBuilder.Entity<Session>().HasDiscriminatorInJsonId();

Fazer isso para todos os tipos de entidade de nível superior fará com que o EF se comporte exatamente como antes.

Neste ponto, se desejar, você também pode atualizar todos os seus documentos para reescrever sua propriedade JSON id. Observe que isso só é possível se entidades de tipos diferentes não compartilharem o mesmo valor de id dentro do mesmo contêiner.

A propriedade JSON id é mapeada para a chave

Problema de rastreamento #34179

Comportamento antigo

Anteriormente, o EF criava uma propriedade sombra mapeada para a propriedade JSON id, a menos que uma das propriedades fosse mapeada explicitamente para id.

Novo comportamento

A partir do EF Core 9, a propriedade chave será mapeada por convenção para a propriedade JSON id, se possível. Isso significa que a propriedade de chave deixará de ser armazenada no documento sob um nome diferente, mantendo o mesmo valor, portanto, o código não-EF que utiliza os documentos e que depende dessa propriedade já não funcionaria corretamente.

Porquê

O EF 9.0 alterou geralmente o mapeamento para estar mais alinhado com as práticas e expectativas comuns de Azure Cosmos DB NoSQL. E não é comum armazenar o valor da chave duas vezes no documento.

Atenuações

Se quiser preservar o comportamento do EF Core 8, a mitigação mais fácil é usar HasShadowId, uma nova opção de configuração que foi introduzida para este fim:

modelBuilder.Entity<Session>().HasShadowId();

Fazer isso para todos os tipos de entidade de nível superior fará com que o EF se comporte exatamente como antes. Ou pode aplicá-lo a todos os tipos de entidades no modelo com uma chamada usando HasShadowIds:

modelBuilder.HasShadowIds();

Alterações de impacto médio

O Sync I/O através do fornecedor Azure Cosmos DB já não é suportado

Problema de rastreamento #32563

Comportamento antigo

Anteriormente, chamar métodos síncronos como ToList ou SaveChanges fazia com que o EF Core bloqueasse de forma síncrona usando .GetAwaiter().GetResult() ao executar chamadas assíncronas contra o Azure Cosmos DB SDK. Isso pode resultar em impasse.

Novo comportamento

A partir do EF Core 9.0, o EF agora lança por padrão ao tentar usar E/S síncronas. A mensagem de exceção é "Azure Cosmos DB não suporta I/O síncrona. Certifique-se de usar e aguardar corretamente apenas métodos assíncronos ao usar o Entity Framework Core para aceder ao Azure Cosmos DB. Consulte https://aka.ms/ef-cosmos-nosync para obter mais informações."

Porquê

O bloqueio síncrono em métodos assíncronos pode resultar em deadlock, e o SDK do Azure Cosmos DB só suporta métodos assíncronos.

Atenuações

No EF Core 9.0, o erro pode ser suprimido com:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.ConfigureWarnings(w => w.Ignore(CosmosEventId.SyncNotSupported));
}

Dito isto, as aplicações deviam deixar de usar APIs de sincronização com o Azure Cosmos DB, já que isto não é suportado pelo Azure Cosmos DB SDK. A capacidade de suprimir a exceção será removida em uma versão futura do EF Core, após a qual a única opção será usar APIs assíncronas.

As consultas SQL agora devem projetar valores JSON diretamente

Acompanhamento do Pedido #25527

Comportamento antigo

Anteriormente, o EF gerava consultas como as seguintes:

SELECT c["City"] FROM root c

Tais consultas fazem com que o Azure Cosmos DB envolva cada resultado num objeto JSON, da seguinte forma:

[
    {
        "City": "Berlin"
    },
    {
        "City": "México D.F."
    }
]
Novo comportamento

A partir do EF Core 9.0, o EF agora adiciona o modificador VALUE às consultas da seguinte maneira:

SELECT VALUE c["City"] FROM root c

Tais consultas fazem com que o Azure Cosmos DB devolva os valores diretamente, sem serem encapsulados:

[
    "Berlin",
    "México D.F."
]

Se seu aplicativo fizer uso de consultas SQL, essas consultas provavelmente serão interrompidas após a atualização para o EF 9.0, pois não incluem o modificador VALUE.

Porquê

Envolver cada resultado num objeto JSON adicional pode causar degradação de desempenho em alguns cenários, inchar a carga útil dos resultados JSON e não é a forma natural de trabalhar com o Azure Cosmos DB.

Atenuações

Para atenuar, basta adicionar o modificador VALUE às projeções de suas consultas SQL, conforme mostrado acima.

Os resultados indefinidos agora são filtrados automaticamente dos resultados da consulta

Acompanhamento do Pedido #25527

Comportamento antigo

Anteriormente, o EF gerava consultas como as seguintes:

SELECT c["City"] FROM root c

Tais consultas fazem com que o Azure Cosmos DB envolva cada resultado num objeto JSON, da seguinte forma:

[
    {
        "City": "Berlin"
    },
    {
        "City": "México D.F."
    }
]

Se qualquer um dos resultados fosse indefinido (por exemplo, a propriedade City estava ausente do documento), um documento vazio era retornado e o EF retornava null para esse resultado.

Novo comportamento

A partir do EF Core 9.0, o EF agora adiciona o modificador VALUE às consultas da seguinte maneira:

SELECT VALUE c["City"] FROM root c

Tais consultas fazem com que o Azure Cosmos DB devolva os valores diretamente, sem serem encapsulados:

[
    "Berlin",
    "México D.F."
]

O comportamento Azure Cosmos DB é filtrar automaticamente os valores de undefined dos resultados; isto significa que, se uma das propriedades City estiver ausente do documento, a consulta devolverá apenas um único resultado, em vez de dois, sendo um deles null.

Porquê

Envolver cada resultado num objeto JSON adicional pode causar degradação de desempenho em alguns cenários, inchar a carga útil dos resultados JSON e não é a forma natural de trabalhar com o Azure Cosmos DB.

Atenuações

Se a obtenção de valores null para resultados indefinidos for importante para seu aplicativo, agrupe os valores de undefined para null usando o novo operador EF.Functions.Coalesce:

var users = await context.Customer
    .Select(c => EF.Functions.CoalesceUndefined(c.City, null))
    .ToListAsync();

As consultas traduzidas incorretamente não são mais traduzidas

Problema de rastreamento #34123

Comportamento antigo

Anteriormente, o EF traduzia consultas como as seguintes:

var sessions = await context.Sessions
    .Take(5)
    .Where(s => s.Name.StartsWith("f"))
    .ToListAsync();

No entanto, a tradução SQL para esta consulta estava incorreta:

SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Session") AND STARTSWITH(c["Name"], "f"))
OFFSET 0 LIMIT @__p_0

Em SQL, a cláusula WHERE é avaliada antes de as cláusulas OFFSET e LIMIT; mas na consulta LINQ acima, o operador Take aparece antes do operador Where. Como resultado, essas consultas podem retornar resultados incorretos.

Novo comportamento

A partir do EF Core 9.0, essas consultas não são mais traduzidas e uma exceção é lançada.

Porquê

Traduções incorretas podem causar corrupção silenciosa de dados, o que pode introduzir bugs difíceis de descobrir em seu aplicativo. O EF sempre prefere falhar rapidamente de forma proativa, agindo imediatamente, em vez de possivelmente causar corrupção de dados.

Atenuações

Se você estava satisfeito com o comportamento anterior e gostaria de executar o mesmo SQL, basta trocar a ordem dos operadores LINQ:

var sessions = await context.Sessions
    .Where(s => s.Name.StartsWith("f"))
    .Take(5)
    .ToListAsync();

Infelizmente, Azure Cosmos DB atualmente não suporta as cláusulas OFFSET e LIMIT nas subconsultas SQL, que é o que a tradução correta da consulta original do LINQ exige.

Alterações de baixo impacto

HasIndex agora lança em vez de ser ignorado

Problema de rastreamento #34023

Comportamento antigo

Anteriormente, as chamadas para HasIndex eram ignoradas pelo provedor do EF Cosmos DB.

Novo comportamento

O provedor agora lança se HasIndex for especificado.

Porquê

No Azure Cosmos DB, todas as propriedades são indexadas por defeito, e não é necessário especificar qualquer indexação. Embora seja possível definir uma política de indexação personalizada, esta não é atualmente suportada pelo EF, podendo ser feita através do Azure Portal sem suporte ao EF. Como as chamadas HasIndex não estavam a fazer nada, deixaram de ser permitidas.

Atenuações

Remova todas as chamadas para HasIndex.

IncludeRootDiscriminatorInJsonId foi renomeado para HasRootDiscriminatorInJsonId após 9.0.0-rc.2

Problema de rastreamento #34717

Comportamento antigo

A API IncludeRootDiscriminatorInJsonId foi introduzida na 9.0.0 rc.1.

Novo comportamento

Para a versão final do EF Core 9.0, a API foi renomeada para HasRootDiscriminatorInJsonId

Porquê

Outra API relacionada foi renomeada para começar com Has em vez de Includee, portanto, esta foi renomeada para consistência também.

Atenuações

Se o seu código estiver usando a API IncludeRootDiscriminatorInJsonId, basta alterá-lo para fazer referência a HasRootDiscriminatorInJsonId em vez disso.

A versão referenciada do Newtonsoft.Json foi atualizada da 10.0.2 para a 13.0.1

Comportamento antigo

O fornecedor Cosmos referiu Newtonsoft.Json versão 10.0.2.

Novo comportamento

A partir do EF Core 9.0, o fornecedor Cosmos faz referência ao Newtonsoft.Json versão 13.0.1.

Porquê

A versão anteriormente referida do Newtonsoft.Json tem vulnerabilidades conhecidas. A versão foi atualizada para evitar depender de uma versão de pacote com problemas de segurança conhecidos.

Atenuações

A atualização para Newtonsoft.Json 13.0.1 não deverá causar problemas na maioria dos casos. Se a sua aplicação usar Newtonsoft.Json diretamente e depender de uma versão mais antiga específica, pode atualizar a sua aplicação para ser compatível com Newtonsoft.Json 13.0.1 ou posterior. Consulte as notas de atualização do Newtonsoft.Json para detalhes sobre alterações entre versões.