Partilhar via


Alterações significativas incluídas no EF Core 3.x

As seguintes alterações de API e comportamento têm o potencial de quebrar aplicativos existentes ao atualizá-los para 3.x. As alterações que esperamos que afetem apenas os provedores de banco de dados são documentadas sob alterações nos provedores.

Resumo

Mudança crítica Impacto
As consultas LINQ já não são avaliadas no lado do cliente Alto
A ferramenta de linha de comando EF Core, dotnet ef, não faz mais parte do SDK do .NET Core Alto
DetectChanges honra os valores-chave gerados pela loja Alto
FromSql, ExecuteSQL e ExecuteSqlAsync foram renomeados Alto
Os tipos de consulta são consolidados com os tipos de entidades Alto
Entity Framework Core não faz mais parte da estrutura compartilhada do ASP.NET Core Médio
exclusões em cascata agora acontecem imediatamente por padrão Médio
O carregamento ansioso de entidades relacionadas agora acontece em uma única consulta Média
DeleteBehavior.Restrict tem semântica mais limpa Média
API de configuração para relações de tipo de propriedade foi alterada Média
Cada propriedade utiliza a geração independente de chaves inteiras em memória Média
consultas sem rastreamento não executam mais a resolução de identidade Médio
Alterações na API de metadados Médio
Alterações na API de metadados específicas do provedor Média
UseRowNumberForPaging foi removido Médio
método FromSql quando usado com procedimento armazenado não pode ser composto Médio
métodos FromSql só podem ser especificados em raízes de consulta Baixo
Os valores de chave temporários não são mais definidos em instâncias de entidade Baixo
As entidades dependentes que partilham a tabela com o principal são agora opcionais Baixo
Todas as entidades que compartilham uma tabela com uma coluna de token de simultaneidade precisam mapeá-la para uma propriedade Baixo
As entidades de propriedade não podem ser consultadas sem que o proprietário use uma consulta de acompanhamento Baixo
As propriedades herdadas de tipos não mapeados agora são mapeadas para uma única coluna para todos os tipos derivados Baixo
A convenção de propriedade de chave estrangeira não corresponde mais ao mesmo nome que a propriedade principal Baixo
A ligação à base de dados é agora fechada se não for mais utilizada antes de o TransactionScope estar concluído Baixo
Os campos de suporte são usados por padrão Baixo
Lançar se vários campos de suporte compatíveis forem encontrados Baixo
Os nomes de propriedade exclusivas de campo devem corresponder ao nome do campo Baixo
AddDbContext/AddDbContextPool não chamam mais AddLogging e AddMemoryCache Baixo
AddEntityFramework* adiciona IMemoryCache com um limite de tamanho Baixo
DbContext.Entry agora realiza uma deteção de alterações local Baixo
As chaves String e byte array não são geradas pelo cliente por padrão Baixo
ILoggerFactory é agora um serviço com escopo Baixo
Os proxies de carregamento lento não assumem mais que as propriedades de navegação estão totalmente carregadas Baixo
A criação excessiva de prestadores de serviços internos é agora um erro por defeito Baixo
Novo comportamento para HasOne/HasMany chamado com uma única string Baixo
O tipo de retorno para vários métodos assíncronos foi alterado de Task para ValueTask Baixo
A anotação Relational:TypeMapping agora é apenas TypeMapping Baixo
ToTable em um tipo derivado lança uma exceção Baixo
EF Core não envia mais pragma para imposição de SQLite FK Baixo
Microsoft.EntityFrameworkCore.Sqlite agora depende de SQLitePCLRaw.bundle_e_sqlite3 Baixo
valores Guid agora são armazenados como TEXTO no SQLite Baixo
Os valores de caracteres agora são armazenados como TEXT no SQLite Baixo
IDs de migração agora são gerados usando o calendário da cultura invariante Baixo
As informações/metadados da extensão foram removidos do IDbContextOptionsExtension Baixo
LogQueryPossibleExceptionWithAggregateOperator foi renomeado para Baixo
Esclarecer API para nomes de restrição de chave estrangeira Baixo
IRelationalDatabaseCreator.HasTables/HasTablesAsync foram tornados públicos Baixo
O Microsoft.EntityFrameworkCore.Design é agora um pacote DevelopmentDependency Baixo
SQLitePCL.raw atualizado para a versão 2.0.0 Baixo
NetTopologySuite atualizado para a versão 2.0.0 Baixo
Microsoft.Data.SqlClient é usado em vez de System.Data.SqlClient Baixo
Múltiplas relações ambíguas de autorreferência devem ser configuradas Baixo
DbFunction.Schema sendo nulo ou string vazia configura-o para estar no esquema padrão do modelo Baixo
EF Core 3.0 destina-se ao .NET Standard 2.1 em vez do .NET Standard 2.0 Revertido
A execução da consulta é registrada no nível de depuração Revertida

Alterações de alto impacto

As consultas LINQ não são mais avaliadas no cliente

Tracking Issue #14935Consulte também a questão #12795

Comportamento antigo

Antes da versão 3.0, quando o EF Core não conseguia converter uma expressão que fazia parte de uma consulta em SQL ou em um parâmetro, ele avaliava automaticamente a expressão no cliente. Por padrão, a avaliação do cliente de expressões potencialmente caras gerou apenas um aviso.

Novo comportamento

A partir da versão 3.0, o EF Core só permite que expressões na projeção de nível superior (a última chamada de Select() feita na consulta) sejam avaliadas no cliente. Quando as expressões em qualquer outra parte da consulta não podem ser convertidas em SQL ou em um parâmetro, uma exceção é lançada.

Porquê

A avaliação automática de consultas pelo cliente permite que muitas consultas sejam executadas, mesmo que partes importantes delas não possam ser traduzidas. Este comportamento pode resultar em comportamentos inesperados e potencialmente prejudiciais que só podem tornar-se evidentes na produção. Por exemplo, uma condição em uma chamada de Where() que não pode ser traduzida pode fazer com que todas as linhas da tabela sejam transferidas do servidor de banco de dados e o filtro seja aplicado no cliente. Essa situação pode facilmente passar despercebida se a tabela contiver apenas algumas linhas durante o desenvolvimento, mas pode ter um impacto significativo quando a aplicação é transferida para a produção, onde a tabela pode conter milhões de linhas. Os avisos de avaliação do cliente também se mostraram muito fáceis de ignorar durante o desenvolvimento.

Além disso, a avaliação automática do utilizador pode levar a problemas em que a melhoria da tradução de consultas para expressões específicas causou alterações disruptivas não intencionais nas diferentes versões.

Atenuações

Se uma consulta não puder ser totalmente traduzida, reescreva a consulta em um formulário que possa ser traduzido ou use AsEnumerableAsync(), ToListAsync()ou similar para trazer explicitamente os dados de volta ao cliente, onde eles podem ser processados usando LINQ-to-Objects.

Alterações de impacto médio

O Entity Framework Core não faz mais parte da estrutura compartilhada do ASP.NET Core

Tracking Issue Announcements#325

Comportamento antigo

Antes do ASP.NET Core 3.0, quando você adicionava uma referência de pacote ao Microsoft.AspNetCore.App ou Microsoft.AspNetCore.All, ele incluía o EF Core e alguns dos provedores de dados do EF Core, como o provedor do SQL Server.

Novo comportamento

A partir da versão 3.0, a estrutura compartilhada do ASP.NET Core não inclui o EF Core nem nenhum provedor de dados do EF Core.

Porquê

Antes dessa alteração, obter o EF Core exigia etapas diferentes, dependendo de se o aplicativo era direcionado para ASP.NET Core e SQL Server, ou não. Além disso, a atualização do ASP.NET Core forçou a atualização do EF Core e do provedor do SQL Server, o que nem sempre é desejável.

Com essa alteração, a experiência de obter o EF Core é a mesma em todos os provedores, implementações .NET suportadas e tipos de aplicativos. Agora, os desenvolvedores também podem controlar exatamente quando o EF Core e os provedores de dados do EF Core são atualizados.

Atenuações

Para usar o EF Core em um aplicativo ASP.NET Core 3.0 ou qualquer outro aplicativo suportado, adicione explicitamente uma referência de pacote ao provedor de banco de dados EF Core que seu aplicativo usará.

A ferramenta de linha de comando EF Core, dotnet ef, não faz mais parte do SDK do .NET Core

Tracking Issue #14016

Comportamento antigo

Antes da versão 3.0, a ferramenta dotnet ef estava incluída no SDK do .NET Core e estava prontamente disponível para uso a partir da linha de comando de qualquer projeto sem exigir etapas extras.

Novo comportamento

A partir da versão 3.0, o SDK do .NET não inclui a ferramenta dotnet ef, portanto, antes de usá-la, é necessário instalá-la explicitamente como uma ferramenta local ou global.

Porquê

Essa alteração nos permite distribuir e atualizar dotnet ef como uma ferramenta regular da CLI do .NET no NuGet, consistente com o fato de que o EF Core 3.0 também é sempre distribuído como um pacote NuGet.

Atenuações

Para poder gerir migrações ou estruturar um DbContext, instale dotnet-ef como uma ferramenta global.

dotnet tool install --global dotnet-ef

Pode também obter uma ferramenta local ao restaurar as dependências de um projeto que a declara como dependência de ferramentas, usando um arquivo de manifesto de ferramentas .

Alterações de baixo impacto

FromSql, ExecuteSQL e ExecuteSqlAsync foram renomeados

Questão de Acompanhamento #10996

Importante

ExecuteSqlCommand e ExecuteSqlCommandAsync foram preteridos. Em vez disso, use esses métodos.

Comportamento antigo

Antes do EF Core 3.0, esses nomes de método eram sobrecarregados para trabalhar com uma cadeia de caracteres normal ou uma cadeia de caracteres que deveria ser interpolada em SQL e parâmetros.

Novo comportamento

A partir do EF Core 3.0, use FromSqlRaw, ExecuteSqlRawe ExecuteSqlRawAsync para criar uma consulta parametrizada onde os parâmetros são passados separadamente da cadeia de caracteres de consulta. Por exemplo:

context.Products.FromSqlRaw(
    "SELECT * FROM Products WHERE Name = {0}",
    product.Name);

Use FromSqlInterpolated, ExecuteSqlInterpolatede ExecuteSqlInterpolatedAsync para criar uma consulta parametrizada onde os parâmetros são passados como parte de uma cadeia de caracteres de consulta interpolada. Por exemplo:

context.Products.FromSqlInterpolated(
    $"SELECT * FROM Products WHERE Name = {product.Name}");

Observe que ambas as consultas acima produzirão o mesmo SQL parametrizado com os mesmos parâmetros SQL.

Porquê

Sobrecargas de método como essa tornam muito fácil chamar acidentalmente o método de cadeia de caracteres bruta quando a intenção era chamar o método de cadeia de caracteres interpolada, e vice-versa. Isso pode fazer com que as consultas não sejam parametrizadas quando deveriam ter sido.

Atenuações

Alterne para usar os novos nomes de método.

O método FromSql quando usado com o procedimento armazenado não pode ser composto

Tracking Issue #15392

Comportamento antigo

Antes do EF Core 3.0, o método FromSql tentava detetar se o SQL fornecido pudesse ser composto. Ele fazia a avaliação do cliente quando o SQL não era componível como um procedimento armazenado. A consulta a seguir funcionou executando o procedimento armazenado no servidor e fazendo FirstOrDefault no lado do cliente.

context.Products.FromSqlRaw("[dbo].[Ten Most Expensive Products]").FirstOrDefault();

Novo comportamento

A partir do EF Core 3.0, o EF Core não tentará analisar o SQL. Portanto, se estiver a compor após o FromSqlRaw/FromSqlInterpolated, o EF Core irá compor o SQL gerando uma subconsulta. Portanto, se você estiver usando um procedimento armazenado com composição, obterá uma exceção para sintaxe SQL inválida.

Porquê

O EF Core 3.0 não suporta a avaliação automática do cliente, uma vez que era propenso a erros, conforme explicado aqui.

Atenuações

Se você estiver usando um procedimento armazenado em FromSqlRaw/FromSqlInterpolated, você sabe que ele não pode ser composto, então você pode adicionar AsEnumerable/AsAsyncEnumerable logo após a chamada do método FromSql para evitar qualquer composição no lado do servidor.

context.Products.FromSqlRaw("[dbo].[Ten Most Expensive Products]").AsEnumerable().FirstOrDefault();

Os métodos FromSql só podem ser especificados em raízes de consulta

Tracking Issue #15704

Comportamento antigo

Antes do EF Core 3.0, o método FromSql podia ser especificado em qualquer lugar na consulta.

Novo comportamento

A partir do EF Core 3.0, os novos métodos FromSqlRaw e FromSqlInterpolated (que substituem FromSql) só podem ser especificados nas raízes de consulta, ou seja, diretamente no DbSet<>. Tentar especificá-los em qualquer outro lugar resultará em um erro de compilação.

Porquê

Especificar FromSql em qualquer outro lugar que não em um DbSet não tinha significado ou valor agregado e poderia causar ambiguidade em certos cenários.

Atenuações

FromSql invocações devem ser movidas para ficarem diretamente sobre o DbSet ao qual se aplicam.

As consultas sem rastreamento não executam mais a resolução de identidade

Tracking Issue #13518

Comportamento antigo

Antes do EF Core 3.0, a mesma instância de entidade era usada para cada ocorrência de uma entidade com um determinado tipo e ID. Isso corresponde ao comportamento das consultas de rastreamento. Por exemplo, esta consulta:

var results = await context.Products.Include(e => e.Category).AsNoTracking().ToListAsync();

retornaria a mesma instância Category para cada Product associado à categoria especificada.

Novo comportamento

A partir do EF Core 3.0, instâncias de entidade diferentes serão criadas quando uma entidade com um determinado tipo e ID for encontrada em locais diferentes no gráfico retornado. Por exemplo, a consulta acima agora retornará uma nova instância de Category para cada Product mesmo quando dois produtos estiverem associados à mesma categoria.

Porquê

A resolução de identidade (ou seja, determinar que uma entidade tem o mesmo tipo e ID que uma entidade encontrada anteriormente) adiciona desempenho adicional e sobrecarga de memória. Isso geralmente vai contra o motivo pelo qual as consultas sem rastreamento são usadas em primeiro lugar. Além disso, embora a resolução de identidade às vezes possa ser útil, ela não é necessária se as entidades forem serializadas e enviadas para um cliente, o que é comum para consultas sem rastreamento.

Atenuações

Utilize uma consulta de rastreamento se a resolução de identidade for necessária.

Os valores de chave temporários não são mais definidos em instâncias de entidade

Tracking Issue #12378

Comportamento antigo

Antes do EF Core 3.0, valores temporários eram atribuídos a todas as propriedades de chave que mais tarde teriam um valor real gerado pelo banco de dados. Normalmente, estes valores temporários eram grandes números negativos.

Novo comportamento

A partir da versão 3.0, o EF Core armazena o valor da chave temporária como parte das informações de rastreamento da entidade e deixa a própria propriedade da chave inalterada.

Porquê

Essa alteração foi feita para evitar que valores de chave temporários se tornem erroneamente permanentes quando uma entidade que foi rastreada anteriormente por alguma instância DbContext é movida para uma instância DbContext diferente.

Atenuações

Os aplicativos que atribuem valores de chave primária a chaves estrangeiras para formar associações entre entidades podem depender do comportamento antigo se as chaves primárias forem geradas por armazenamento e pertencerem a entidades no estado Added. Isto pode ser evitado mediante:

  • Não usar chaves geradas pela loja.
  • Definir propriedades de navegação para formar relações em vez de definir valores de chave estrangeira.
  • Obtenha os valores de chave temporários reais das informações de rastreio da entidade. Por exemplo, context.Entry(blog).Property(e => e.Id).CurrentValue retornará o valor temporário mesmo que blog.Id em si não tenha sido definido.

DetectChanges honra os valores-chave gerados pela loja

Tracking Issue #14616

Comportamento antigo

Antes do EF Core 3.0, uma entidade não rastreada encontrada por DetectChanges era rastreada no estado Added e inserida como uma nova linha quando SaveChanges é chamada.

Novo comportamento

A partir do EF Core 3.0, se uma entidade estiver usando valores de chave gerados e algum valor de chave for definido, a entidade será rastreada no estado Modified. Isso significa que se assume a existência de uma linha para a entidade, que será atualizada quando SaveChanges for chamado. Se o valor da chave não estiver definido, ou se o tipo de entidade não estiver usando chaves geradas, a nova entidade ainda será rastreada Added como nas versões anteriores.

Porquê

Essa alteração foi feita para tornar mais fácil e consistente trabalhar com gráficos de entidade desconectados ao usar chaves geradas pela loja.

Atenuações

Essa alteração pode quebrar um aplicativo se um tipo de entidade estiver configurado para usar chaves geradas, mas os valores de chave forem explicitamente definidos para novas instâncias. A correção é configurar explicitamente as propriedades da chave para não usar valores gerados. Por exemplo, com a API fluente:

modelBuilder
    .Entity<Blog>()
    .Property(e => e.Id)
    .ValueGeneratedNever();

Ou com anotações de dados:

[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }

As exclusões em cascata agora acontecem imediatamente por padrão

Problema de rastreamento #10114

Comportamento antigo

Antes da versão 3.0, o EF Core aplicava ações em cascata (eliminando entidades dependentes quando uma entidade principal obrigatória era excluída ou quando a relação com uma entidade principal obrigatória era cortada) somente quando SaveChanges era chamado.

Novo comportamento

A partir da versão 3.0, o EF Core aplica ações em cascata assim que a condição de acionamento é detetada. Por exemplo, chamar context.Remove() para eliminar uma entidade principal resultará em que todas as entidades dependentes obrigatórias relacionadas e rastreadas sejam também definidas para Deleted imediatamente.

Porquê

Essa alteração foi feita para melhorar a experiência em cenários de vinculação e auditoria de dados, onde é importante entender quais entidades serão excluídas antes queSaveChanges seja chamada.

Atenuações

O comportamento anterior pode ser restaurado através de configurações no context.ChangeTracker. Por exemplo:

context.ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;

Problema de rastreamento #18022

Comportamento antigo

Antes da versão 3.0, o carregamento ávido de navegações de coleta por meio de operadores de Include fazia com que várias consultas fossem geradas no banco de dados relacional, uma para cada tipo de entidade relacionada.

Novo comportamento

A partir da versão 3.0, o EF Core gera uma única consulta com JOINs em bancos de dados relacionais.

Porquê

A emissão de várias consultas para implementar uma única consulta LINQ causou vários problemas, incluindo desempenho negativo, pois várias viagens de ida e volta do banco de dados eram necessárias, e problemas de coerência de dados, pois cada consulta podia observar um estado diferente do banco de dados.

Atenuações

Embora tecnicamente essa não seja uma alteração de rutura, ela pode ter um efeito considerável no desempenho do aplicativo quando uma única consulta contém um grande número de operadores de Include em navegações de coleção. Consulte este comentário para obter mais informações e para reescrever consultas de uma forma mais eficiente.

**

DeleteBehavior.Restrict tem semântica mais limpa

Questão de Rastreio #12661

Comportamento antigo

Antes da versão 3.0, DeleteBehavior.Restrict criava chaves estrangeiras na base de dados com semântica Restrict, mas também alterava o ajuste interno de uma maneira pouco evidente.

Novo comportamento

A partir da versão 3.0, DeleteBehavior.Restrict garante que as chaves estrangeiras sejam criadas com semântica Restrict, ou seja, sem cascatas; gerar erro em caso de violação de restrição - sem também afetar o ajuste interno do EF.

Porquê

Esta alteração foi feita para melhorar a experiência de utilização do DeleteBehavior de uma forma intuitiva, sem efeitos secundários inesperados.

Atenuações

O comportamento anterior pode ser restaurado usando DeleteBehavior.ClientNoAction.

Os tipos de consulta são consolidados com tipos de entidade

Tracking Issue #14194

Comportamento antigo

Antes do EF Core 3.0, tipos de consulta eram um meio de consultar dados que não definem uma chave primária de forma estruturada. Ou seja, um tipo de consulta foi utilizado para mapear tipos de entidade sem chaves (mais provavelmente a partir de uma vista, mas possivelmente de uma tabela), enquanto um tipo de entidade regular foi utilizado quando uma chave estava disponível (mais provavelmente de uma tabela, mas possivelmente de uma vista).

Novo comportamento

Um tipo de consulta agora se torna apenas um tipo de entidade sem uma chave primária. Os tipos de entidade sem chave têm a mesma funcionalidade que os tipos de consulta em versões anteriores.

Porquê

Essa alteração foi feita para reduzir a confusão em torno da finalidade dos tipos de consulta. Especificamente, são tipos de entidades sem chave e são inerentemente de leitura apenas, mas não devem ser usados apenas pelo facto de um tipo de entidade precisar ser de leitura apenas. Da mesma forma, eles geralmente são mapeados para modos de exibição, mas isso ocorre apenas porque os modos de exibição geralmente não definem chaves.

Atenuações

As seguintes partes da API estão obsoletas:

  • ModelBuilder.Query<>() - Em vez disso, ModelBuilder.Entity<>().HasNoKey() precisa ser chamado para marcar um tipo de entidade como não tendo chaves. Isso ainda não seria configurado por convenção para evitar erros de configuração quando uma chave primária é esperada, mas não corresponde à convenção.
  • DbQuery<> - Em vez disso, DbSet<> deve ser usado.
  • DbContext.Query<>() - Em vez disso, DbContext.Set<>() deve ser usado.
  • IQueryTypeConfiguration<TQuery> - Em vez disso, IEntityTypeConfiguration<TEntity> deve ser usado.

Observação

Devido a um problema no 3.x ao consultar entidades sem chave que têm todas as propriedades definidas como null, será retornado um null em vez de uma entidade. Se este problema for aplicável ao seu cenário, também adicione lógica para lidar com null nos resultados.

A API de configuração para relações de tipo de propriedade foi alterada

Problema de rastreamento #12444Problema de rastreamento #9148Problema de rastreamento #14153

Comportamento antigo

Antes do EF Core 3.0, a configuração do relacionamento de propriedade era realizada diretamente após a chamada OwnsOne ou OwnsMany.

Novo comportamento

A partir do EF Core 3.0, há agora uma API fluente para configurar uma propriedade de navegação para o proprietário usando WithOwner(). Por exemplo:

modelBuilder.Entity<Order>.OwnsOne(e => e.Details).WithOwner(e => e.Order);

A configuração relacionada à relação entre proprietário e possuído agora deve ser encadeada após WithOwner(), semelhante a outras relações configuradas. Enquanto a configuração para o tipo possuído em si ainda estaria encadeada após OwnsOne()/OwnsMany(). Por exemplo:

modelBuilder.Entity<Order>.OwnsOne(e => e.Details, eb =>
    {
        eb.WithOwner()
            .HasForeignKey(e => e.AlternateId)
            .HasConstraintName("FK_OrderDetails");

        eb.ToTable("OrderDetails");
        eb.HasKey(e => e.AlternateId);
        eb.HasIndex(e => e.Id);

        eb.HasOne(e => e.Customer).WithOne();

        eb.HasData(
            new OrderDetails
            {
                AlternateId = 1,
                Id = -1
            });
    });

Além disso, chamar Entity(), HasOne()ou Set() com um destino de tipo próprio agora lançará uma exceção.

Porquê

Essa alteração foi feita para criar uma separação mais clara entre a configuração do próprio tipo possuído e a relação com e desse tipo possuído. Isto, por sua vez, elimina a ambiguidade e a confusão em torno de métodos como HasForeignKey.

Atenuações

Altere a configuração das relações de tipo de propriedade para usar a nova superfície da API, conforme mostrado no exemplo acima.

As entidades dependentes que partilham a tabela com a entidade principal são opcionais agora.

Tracking Issue #9005

Comportamento antigo

Considere o seguinte modelo:

public class Order
{
    public int Id { get; set; }
    public int CustomerId { get; set; }
    public OrderDetails Details { get; set; }
}

public class OrderDetails
{
    public int Id { get; set; }
    public string ShippingAddress { get; set; }
}

Antes do EF Core 3.0, se OrderDetails fosse propriedade de Order ou fosse explicitamente mapeado na mesma tabela, uma instância OrderDetails sempre era necessária ao adicionar um novo Order.

Novo comportamento

A partir da versão 3.0, o EF Core permite adicionar uma Order sem uma OrderDetails e mapeia todas as propriedades OrderDetails para colunas anuláveis, exceto a chave primária. Ao consultar o EF Core define OrderDetails para null se alguma de suas propriedades necessárias não tem um valor ou se não tem propriedades necessárias além da chave primária e todas as propriedades são null.

Atenuações

Se o seu modelo tiver um compartilhamento de tabela dependente com todas as colunas opcionais, mas a navegação apontando para ele não deve ser null então o aplicativo deve ser modificado para lidar com casos quando a navegação estiver null. Se isso não for possível, uma propriedade necessária deve ser adicionada ao tipo de entidade ou pelo menos uma propriedade deve ter um valor nãonull atribuído a ela.

Todas as entidades que partilham uma tabela com uma coluna de marcador de concorrência têm de mapeá-la para uma propriedade.

Tracking Issue #14154

Comportamento antigo

Considere o seguinte modelo:

public class Order
{
    public int Id { get; set; }
    public int CustomerId { get; set; }
    public byte[] Version { get; set; }
    public OrderDetails Details { get; set; }
}

public class OrderDetails
{
    public int Id { get; set; }
    public string ShippingAddress { get; set; }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Order>()
        .Property(o => o.Version).IsRowVersion().HasColumnName("Version");
}

Antes do EF Core 3.0, se OrderDetails pertencer a Order ou for explicitamente mapeado para a mesma tabela, atualizar apenas OrderDetails não atualizará o valor de Version no cliente e a próxima atualização irá falhar.

Novo comportamento

A partir da versão 3.0, EF Core propaga o novo valor Version para Order se tiver OrderDetails. Caso contrário, uma exceção será lançada durante a validação do modelo.

Porquê

Essa alteração foi feita para evitar um valor de token de simultaneidade obsoleto quando apenas uma das entidades mapeadas para a mesma tabela é atualizada.

Atenuações

Todas as entidades que compartilham a tabela devem incluir uma propriedade que esteja mapeada com a coluna do token de simultaneidade. É possível criar um em estado de sombra:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<OrderDetails>()
        .Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
}

As entidades de propriedade não podem ser consultadas sem que o proprietário use uma consulta de acompanhamento

Tracking Issue #18876

Comportamento antigo

Antes do EF Core 3.0, as entidades proprietárias podiam ser consultadas como qualquer outra navegação.

context.People.Select(p => p.Address);

Novo comportamento

A partir da versão 3.0, o EF Core irá lançar uma exceção se uma consulta de rastreamento projetar uma entidade subordinada sem o seu proprietário.

Porquê

As entidades possuídas não podem ser manipuladas sem o seu proprietário, pelo que, na grande maioria dos casos, consultá-las desta forma é um erro.

Atenuações

Se a entidade de propriedade deve ser rastreada para ser modificada de alguma forma mais tarde, então o proprietário deve ser incluído na consulta.

Caso contrário, adicione uma chamada AsNoTracking():

context.People.Select(p => p.Address).AsNoTracking();

As propriedades herdadas de tipos não mapeados agora são mapeadas para uma única coluna para todos os tipos derivados

Questão de Rastreamento #13998

Comportamento antigo

Considere o seguinte modelo:

public abstract class EntityBase
{
    public int Id { get; set; }
}

public abstract class OrderBase : EntityBase
{
    public int ShippingAddress { get; set; }
}

public class BulkOrder : OrderBase
{
}

public class Order : OrderBase
{
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Ignore<OrderBase>();
    modelBuilder.Entity<EntityBase>();
    modelBuilder.Entity<BulkOrder>();
    modelBuilder.Entity<Order>();
}

Antes do EF Core 3.0, a propriedade ShippingAddress era mapeada para colunas separadas para BulkOrder e Order por padrão.

Novo comportamento

A partir da versão 3.0, o EF Core cria apenas uma coluna para ShippingAddress.

Porquê

O comportamento antigo foi inesperado.

Atenuações

A propriedade ainda pode ser explicitamente mapeada para uma coluna separada nos tipos derivados.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Ignore<OrderBase>();
    modelBuilder.Entity<EntityBase>();
    modelBuilder.Entity<BulkOrder>()
        .Property(o => o.ShippingAddress).HasColumnName("BulkShippingAddress");
    modelBuilder.Entity<Order>()
        .Property(o => o.ShippingAddress).HasColumnName("ShippingAddress");
}

A convenção de propriedade de chave estrangeira já não tem o mesmo nome que a propriedade principal

Tracking Issue #13274

Comportamento antigo

Considere o seguinte modelo:

public class Customer
{
    public int CustomerId { get; set; }
    public ICollection<Order> Orders { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public int CustomerId { get; set; }
}

Antes do EF Core 3.0, a propriedade CustomerId era utilizada como chave estrangeira por convenção. No entanto, se Order é um tipo possuído, então isto também tornaria CustomerId a chave primária, o que não corresponde normalmente à expectativa.

Novo comportamento

A partir da versão 3.0, o EF Core não tenta usar propriedades para chaves estrangeiras por convenção se elas tiverem o mesmo nome da propriedade principal. O nome do tipo principal concatenado com o nome da propriedade principal, e o nome da navegação concatenado com o nome da propriedade principal, ainda correspondem aos padrões. Por exemplo:

public class Customer
{
    public int Id { get; set; }
    public ICollection<Order> Orders { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public int CustomerId { get; set; }
}
public class Customer
{
    public int Id { get; set; }
    public ICollection<Order> Orders { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public int BuyerId { get; set; }
    public Customer Buyer { get; set; }
}

Porquê

Essa alteração foi feita para evitar a definição errada de uma propriedade de chave primária no tipo possuído.

Atenuações

Se a propriedade foi destinada a ser a chave estrangeira e, portanto, parte da chave primária, configure-a explicitamente como tal.

A conexão do banco de dados agora é fechada se não for mais usada antes que o TransactionScope tenha sido concluído

Tracking Issue #14218

Comportamento antigo

Antes do EF Core 3.0, se o contexto abrir a conexão dentro de um TransactionScope, a conexão permanecerá aberta enquanto o TransactionScope atual estiver ativo.

using (new TransactionScope())
{
    using (AdventureWorks context = new AdventureWorks())
    {
        context.ProductCategories.Add(new ProductCategory());
        await context.SaveChangesAsync();

        // Old behavior: Connection is still open at this point

        var categories = await context.ProductCategories().ToListAsync();
    }
}

Novo comportamento

A partir da versão 3.0, o EF Core fecha a conexão assim que termina de usá-la.

Porquê

Esta alteração permite utilizar vários contextos no mesmo TransactionScope. O novo comportamento também corresponde ao EF6.

Atenuações

Se a conexão precisar permanecer aberta, a chamada explícita para OpenConnection() garantirá que o EF Core não a feche prematuramente:

using (new TransactionScope())
{
    using (AdventureWorks context = new AdventureWorks())
    {
        await context.Database.OpenConnectionAsync();
        context.ProductCategories.Add(new ProductCategory());
        await context.SaveChangesAsync();

        var categories = await context.ProductCategories().ToListAsync();
        await context.Database.CloseConnectionAsync();
    }
}

Cada propriedade utiliza geração independente de chaves inteiras na memória

Tracking Issue #6872

Comportamento antigo

Antes do EF Core 3.0, um gerador de valor compartilhado era usado para todas as propriedades de chave inteira na memória.

Novo comportamento

A partir do EF Core 3.0, cada propriedade de chave inteira obtém seu próprio gerador de valor ao usar o banco de dados na memória. Além disso, se o banco de dados for excluído, a geração de chaves será redefinida para todas as tabelas.

Porquê

Essa alteração foi feita para alinhar a geração de chaves na memória mais de perto com a geração de chaves de banco de dados real e para melhorar a capacidade de isolar testes uns dos outros ao usar o banco de dados na memória.

Atenuações

Isso pode quebrar um aplicativo que depende de valores de chave na memória específicos a serem definidos. Em vez disso, considere não confiar em valores de chave específicos ou atualizar para corresponder ao novo comportamento.

Os campos de suporte são usados por padrão

Problema de Rastreamento #12430

Comportamento antigo

Antes da versão 3.0, mesmo que o campo de apoio de uma propriedade fosse conhecido, o EF Core ainda lia e escrevia, por padrão, o valor da propriedade usando os métodos getter e setter da propriedade. A exceção a isso era a execução da consulta, onde o campo de suporte seria definido diretamente, se conhecido.

Novo comportamento

A partir do EF Core 3.0, se o campo de suporte de uma propriedade for conhecido, o EF Core sempre lerá e gravará essa propriedade usando o campo de suporte. Isso pode causar uma quebra de aplicativo se o aplicativo estiver confiando em comportamento adicional codificado nos métodos getter ou setter.

Porquê

Essa alteração foi feita para evitar que o EF Core acionasse erroneamente a lógica de negócios por padrão ao executar operações de banco de dados envolvendo as entidades.

Atenuações

O comportamento pré-3.0 pode ser restaurado através da configuração do modo de acesso à propriedade no ModelBuilder. Por exemplo:

modelBuilder.UsePropertyAccessMode(PropertyAccessMode.PreferFieldDuringConstruction);

Lance se vários campos de suporte compatíveis forem encontrados

Tracking Issue #12523

Comportamento antigo

Antes do EF Core 3.0, se vários campos correspondessem às regras para encontrar o campo de suporte de uma propriedade, um campo seria escolhido com base em alguma ordem de precedência. Isso pode fazer com que o campo errado seja usado em casos ambíguos.

Novo comportamento

A partir do EF Core 3.0, se vários campos forem associados à mesma propriedade, será lançada uma exceção.

Porquê

Esta alteração foi feita para evitar o uso silencioso de um campo sobre outro quando apenas um pode estar correto.

Atenuações

As propriedades com campos de suporte ambíguos devem ter o campo a ser usado especificado explicitamente. Por exemplo, usando a API fluente:

modelBuilder
    .Entity<Blog>()
    .Property(e => e.Id)
    .HasField("_id");

Os nomes de campo apenas de propriedade devem corresponder ao nome do campo

Comportamento antigo

Antes do EF Core 3.0, uma propriedade podia ser especificada por um valor de cadeia de caracteres e, se nenhuma propriedade com esse nome fosse encontrada no tipo .NET, o EF Core tentaria correspondê-la a um campo usando regras de convenção.

private class Blog
{
    private int _id;
    public string Name { get; set; }
}
modelBuilder
    .Entity<Blog>()
    .Property("Id");

Novo comportamento

A partir do EF Core 3.0, uma propriedade exclusiva de campo deve corresponder exatamente ao nome do campo.

modelBuilder
    .Entity<Blog>()
    .Property("_id");

Porquê

Essa alteração foi feita para evitar o uso do mesmo campo para duas propriedades nomeadas de forma semelhante, ela também torna as regras de correspondência para propriedades somente de campo as mesmas que para propriedades mapeadas para propriedades CLR.

Atenuações

As propriedades exclusivas de campo devem ter o mesmo nome que o campo ao qual estão mapeadas. Em uma versão futura do EF Core após a versão 3.0, planejamos reativar a configuração explícita de um nome de campo diferente do nome da propriedade (consulte o problema #15307):

modelBuilder
    .Entity<Blog>()
    .Property("Id")
    .HasField("_id");

AddDbContext/AddDbContextPool não chama mais AddLogging e AddMemoryCache

Tracking Issue #14756

Comportamento antigo

Antes do EF Core 3.0, chamar AddDbContext ou AddDbContextPool também registava serviços de registo de logs e cache de memória com DI, através de chamadas para AddLogging e AddMemoryCache.

Novo comportamento

A partir do EF Core 3.0, AddDbContext e AddDbContextPool não registrarão mais esses serviços com a injeção de dependência (DI).

Porquê

O EF Core 3.0 não requer que esses serviços estejam no contêiner DI do aplicativo. No entanto, se ILoggerFactory estiver registrado no contêiner DI do aplicativo, ele ainda será usado pelo EF Core.

Atenuações

Se seu aplicativo precisar desses serviços, registre-os explicitamente no contêiner DI usando AddLogging ou AddMemoryCache.

AddEntityFramework* adiciona IMemoryCache com um limite de tamanho

Tracking Issue #12905

Comportamento antigo

Antes do EF Core 3.0, chamar AddEntityFramework* métodos também registrava serviços de cache de memória com DI sem um limite de tamanho.

Novo comportamento

A partir do EF Core 3.0, AddEntityFramework* registrará um serviço IMemoryCache com um limite de tamanho. Se quaisquer outros serviços adicionados posteriormente dependerem do IMemoryCache, eles podem atingir rapidamente o limite padrão, causando exceções ou desempenho degradado.

Porquê

Usar IMemoryCache sem um limite pode resultar em uso descontrolado de memória se houver um bug na lógica de cache de consulta ou se as consultas forem geradas dinamicamente. Ter um limite padrão atenua um possível ataque DoS.

Atenuações

Na maioria dos casos, ligar para AddEntityFramework* não é necessário se AddDbContext ou AddDbContextPool também for chamado. Portanto, a melhor mitigação é remover a chamada AddEntityFramework*.

Se seu aplicativo precisar desses serviços, registre uma implementação IMemoryCache explicitamente com o contêiner DI de antemão usando AddMemoryCache.

DbContext.Entry agora executa um DetectChanges local

Tracking Issue #13552

Comportamento antigo

Antes do EF Core 3.0, chamar DbContext.Entry fazia com que alterações fossem detetadas para todas as entidades rastreadas. Isso garantiu que o estado exposto no EntityEntry estivesse up-todata.

Novo comportamento

A partir do EF Core 3.0, chamar DbContext.Entry agora só tentará detetar alterações na entidade específica e em quaisquer entidades principais controladas relacionadas a ela. Isso significa que as alterações em outros lugares podem não ter sido detetadas chamando esse método, o que pode ter implicações no estado do aplicativo.

Observe que, se ChangeTracker.AutoDetectChangesEnabled estiver definido como false até mesmo essa deteção de alteração local será desabilitada.

Outros métodos que causam deteção de alterações - por exemplo, ChangeTracker.Entries e SaveChanges- ainda causam uma DetectChanges completa de todas as entidades rastreadas.

Porquê

Essa alteração foi feita para melhorar o desempenho padrão do uso do context.Entry.

Atenuações

Chame ChangeTracker.DetectChanges() explicitamente antes de chamar Entry para garantir o comportamento pré-3.0.

As chaves String e array de bytes não são geradas pelo cliente por padrão

Tracking Issue #14617

Comportamento antigo

Antes do EF Core 3.0, string e byte[] propriedades de chave podiam ser usadas sem definir explicitamente um valor não nulo. Nesse caso, o valor da chave seria gerado no cliente sob a forma de um GUID, e depois serializado em bytes para byte[].

Novo comportamento

A partir do EF Core 3.0, será lançada uma exceção indicando que nenhum valor de chave foi definido.

Porquê

Essa alteração foi feita porque os valores de string/byte[] gerados pelo cliente geralmente não são úteis, e o comportamento padrão tornou difícil raciocinar sobre os valores de chave gerados de uma maneira comum.

Atenuações

O comportamento pré-3.0 pode ser obtido especificando explicitamente que as propriedades de chave devem usar valores gerados se nenhum outro valor não nulo for definido. Por exemplo, com a API fluente:

modelBuilder
    .Entity<Blog>()
    .Property(e => e.Id)
    .ValueGeneratedOnAdd();

Ou com anotações de dados:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }

ILoggerFactory é agora um serviço com escopo

Tracking Issue #14698

Comportamento antigo

Antes do EF Core 3.0, ILoggerFactory era registado como um serviço singleton.

Novo comportamento

A partir do EF Core 3.0, ILoggerFactory agora está registrado como escopo.

Porquê

Esta alteração foi feita para permitir a associação de um registador com uma instância DbContext, o que permite outras funcionalidades e elimina alguns casos de comportamento patológico, como uma explosão de prestadores de serviços internos.

Atenuações

Essa alteração não deve afetar o código do aplicativo, a menos que ele esteja registrando e usando serviços personalizados no provedor de serviços interno do EF Core. Isso não é comum. Nesses casos, a maioria das coisas ainda funcionará, mas qualquer serviço singleton que dependia de ILoggerFactory precisará ser alterado para obter o ILoggerFactory de forma diferente.

Se você se deparar com situações como esta, registre um problema no do rastreador de problemas do EF Core GitHub para nos informar como você está usando ILoggerFactory para que possamos entender melhor como não quebrar isso novamente no futuro.

Os proxies de carregamento lento não assumem mais que as propriedades de navegação estão totalmente carregadas

Problema de rastreamento #12780

Comportamento antigo

Antes do EF Core 3.0, uma vez que um DbContext era descartado, não havia como saber se uma determinada propriedade de navegação em uma entidade obtida a partir desse contexto estava totalmente carregada ou não. Em vez disso, os proxies assumem que uma navegação de referência é carregada se tiver um valor não nulo e que uma navegação de coleção é carregada se não estiver vazia. Nestes casos, tentar fazer um carregamento lento seria uma no-op.

Novo comportamento

A partir do EF Core 3.0, os proxies controlam se uma propriedade de navegação é carregada ou não. Isso significa que tentar acessar uma propriedade de navegação que é carregada depois que o contexto foi descartado sempre será um no-op, mesmo quando a navegação carregada estiver vazia ou nula. Por outro lado, tentar acessar uma propriedade de navegação que não está carregada lançará uma exceção se o contexto for descartado, mesmo que a propriedade de navegação seja uma coleção não vazia. Se essa situação surgir, isso significa que o código do aplicativo está tentando usar o carregamento lento em um momento inválido, e o aplicativo deve ser alterado para não fazer isso.

Porquê

Essa alteração foi feita para tornar o comportamento consistente e correto ao tentar carregar preguiçosamente em uma instância DbContext descartada.

Atenuações

Atualize o código do aplicativo para não tentar carregar com lentidão com um contexto descartado ou configure isso para ser um no-op conforme descrito na mensagem de exceção.

A criação excessiva de prestadores de serviços internos é agora um erro por defeito

Problema de rastreamento #10236

Comportamento antigo

** Antes do EF Core 3.0, um aviso seria registado quando um aplicativo criava um número patológico de fornecedores de serviços internos.

Novo comportamento

A partir do EF Core 3.0, esse aviso agora é considerado um erro e uma exceção é lançada.

Porquê

Esta alteração foi feita para melhorar o código da aplicação, expondo este caso patológico de forma mais explícita.

Atenuações

A causa mais apropriada de ação ao encontrar esse erro é entender a causa raiz e parar de criar tantos provedores de serviços internos. No entanto, o erro pode ser convertido novamente em um aviso (ou ignorado) através da configuração no DbContextOptionsBuilder. Por exemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .ConfigureWarnings(w => w.Log(CoreEventId.ManyServiceProvidersCreatedWarning));
}

Novo comportamento para HasOne/HasMany quando chamado com uma única string

Questão de Acompanhamento #9171

Comportamento antigo

Antes do EF Core 3.0, a chamada de código HasOne ou HasMany com uma única cadeia de caracteres era interpretada de forma confusa. Por exemplo:

modelBuilder.Entity<Samurai>().HasOne("Entrance").WithOne();

O código parece estar relacionado Samurai a algum outro tipo de entidade usando a propriedade Entrance navigation, que pode ser privada.

Na realidade, esse código tenta criar uma relação com algum tipo de entidade chamada Entrance sem nenhuma propriedade de navegação.

Novo comportamento

Começando com o EF Core 3.0, o código acima agora faz o que parecia que deveria ter feito antes.

Porquê

O comportamento antigo era muito confuso, especialmente ao ler o código de configuração e procurar erros.

Atenuações

Isso quebrará apenas os aplicativos que estão configurando explicitamente relacionamentos usando cadeias de caracteres para nomes de tipo e sem especificar a propriedade de navegação explicitamente. Isso não é comum. O comportamento anterior pode ser obtido através da passagem explícita de null para o nome da propriedade de navegação. Por exemplo:

modelBuilder.Entity<Samurai>().HasOne("Some.Entity.Type.Name", null).WithOne();

O tipo de retorno para vários métodos assíncronos foi alterado de Task para ValueTask

Rastreamento de Problema #15184

Comportamento antigo

Os seguintes métodos assíncronos retornaram anteriormente um Task<T>:

  • DbContext.FindAsync()
  • DbSet.FindAsync()
  • DbContext.AddAsync()
  • DbSet.AddAsync()
  • ValueGenerator.NextValueAsync() (e classes derivadas)

Novo comportamento

Os métodos mencionados acima agora retornam um ValueTask<T> sobre o mesmo T que antes.

Porquê

Essa alteração reduz o número de alocações de heap incorridas ao invocar esses métodos, melhorando o desempenho geral.

Atenuações

Os aplicativos que simplesmente aguardam as APIs acima só precisam ser recompilados - nenhuma alteração de origem é necessária. Um uso mais complexo (por exemplo, passar o Task retornado para Task.WhenAny()) normalmente requer que o ValueTask<T> retornado seja convertido em um Task<T> chamando AsTask() nele. Note-se que isto anula a redução de alocação que esta alteração traz.

A anotação Relational:TypeMapping agora é apenas TypeMapping

Tracking Issue #9913

Comportamento antigo

O nome da anotação para anotações de mapeamento de tipo era "Relational:TypeMapping".

Novo comportamento

O nome da anotação para anotações de mapeamento de tipo agora é "TypeMapping".

Porquê

Os mapeamentos de tipo agora são usados para mais do que apenas provedores de banco de dados relacional.

Atenuações

Isso só interromperá os aplicativos que acessam o mapeamento de tipo diretamente como uma anotação, o que não é comum. A ação mais apropriada a ser corrigida é usar a superfície da API para acessar mapeamentos de tipo em vez de usar a anotação diretamente.

ToTable em um tipo derivado lança uma exceção

Tracking Issue #11811

Comportamento antigo

Antes do EF Core 3.0, o ToTable() chamado num tipo derivado seria desconsiderado, pois a única estratégia de mapeamento de herança era TPH, no qual isso não é aplicável.

Novo comportamento

A partir do EF Core 3.0 e em preparação para adicionar suporte a TPT e TPC numa versão posterior, a chamada de ToTable() em um tipo derivado agora gerará uma exceção para evitar uma alteração de mapeamento inesperada no futuro.

Porquê

Atualmente, não é válido mapear um tipo derivado para uma tabela diferente. Esta mudança evita ruturas no futuro, quando se torna uma coisa válida a fazer.

Atenuações

Remova todas as tentativas de mapear tipos derivados para outras tabelas.

ForSqlServerHasIndex substituído por HasIndex

Tracking Issue #12366

Comportamento antigo

Antes do EF Core 3.0, ForSqlServerHasIndex().ForSqlServerInclude() fornecia uma maneira de configurar colunas usadas com INCLUDE.

Novo comportamento

A partir do EF Core 3.0, o uso de Include em um índice agora é suportado no nível relacional. Utilize HasIndex().ForSqlServerInclude().

Porquê

Essa alteração foi feita para consolidar a API para índices com Include em um só lugar para todos os provedores de banco de dados.

Atenuações

Use a nova API, como mostrado acima.

Alterações na API de metadados

Tracking Issue #214

Novo comportamento

As seguintes propriedades foram convertidas em métodos de extensão:

  • IEntityType.QueryFilter ->GetQueryFilter()
  • IEntityType.DefiningQuery ->GetDefiningQuery()
  • IProperty.IsShadowProperty ->IsShadowProperty()
  • IProperty.BeforeSaveBehavior ->GetBeforeSaveBehavior()
  • IProperty.AfterSaveBehavior ->GetAfterSaveBehavior()

Porquê

Esta alteração simplifica a implementação das interfaces acima mencionadas.

Atenuações

Use os novos métodos de extensão.

Alterações na API de metadados específicas do provedor

Tracking Issue #214

Novo comportamento

Os métodos de extensão específicos do provedor serão simplificados.

  • IProperty.Relational().ColumnName ->IProperty.GetColumnName()
  • IEntityType.SqlServer().IsMemoryOptimized ->IEntityType.IsMemoryOptimized()
  • PropertyBuilder.UseSqlServerIdentityColumn() ->PropertyBuilder.UseIdentityColumn()

Porquê

Esta alteração simplifica a implementação dos métodos de extensão acima mencionados.

Atenuações

Use os novos métodos de extensão.

O EF Core não envia mais pragma para imposição de SQLite FK

Tracking Issue #12151

Comportamento antigo

Antes do EF Core 3.0, o EF Core enviava PRAGMA foreign_keys = 1 quando uma conexão com o SQLite é aberta.

Novo comportamento

A partir do EF Core 3.0, o EF Core não envia mais PRAGMA foreign_keys = 1 quando uma conexão com o SQLite é aberta.

Porquê

Essa alteração foi feita porque o EF Core usa SQLitePCLRaw.bundle_e_sqlite3 por padrão, o que, por sua vez, significa que a imposição de FK está ativada por padrão e não precisa ser explicitamente habilitada cada vez que uma conexão é aberta.

Atenuações

As chaves estrangeiras são habilitadas por padrão no SQLitePCLRaw.bundle_e_sqlite3, que é usado por padrão para o EF Core. Para outros casos, as chaves estrangeiras podem ser habilitadas especificando Foreign Keys=True na cadeia de conexão.

Microsoft.EntityFrameworkCore.Sqlite agora depende de SQLitePCLRaw.bundle_e_sqlite3

Comportamento antigo

Antes do EF Core 3.0, o EF Core usava SQLitePCLRaw.bundle_green.

Novo comportamento

A partir do EF Core 3.0, o EF Core usa SQLitePCLRaw.bundle_e_sqlite3.

Porquê

Esta alteração foi feita para que a versão do SQLite usada no iOS seja consistente com outras plataformas.

Atenuações

Para usar a versão nativa do SQLite no iOS, configure Microsoft.Data.Sqlite para usar um pacote de SQLitePCLRaw diferente.

Os valores Guid agora são armazenados como TEXT no SQLite

Tracking Issue #15078

Comportamento antigo

Os valores Guid foram previamente armazenados como valores BLOB no SQLite.

Novo comportamento

Os valores Guid agora são armazenados como TEXT.

Porquê

O formato binário do Guids não é padronizado. Armazenar os valores como TEXT torna o banco de dados mais compatível com outras tecnologias.

Atenuações

Você pode migrar bancos de dados existentes para o novo formato executando SQL da seguinte forma.

UPDATE MyTable
SET GuidColumn = hex(substr(GuidColumn, 4, 1)) ||
                 hex(substr(GuidColumn, 3, 1)) ||
                 hex(substr(GuidColumn, 2, 1)) ||
                 hex(substr(GuidColumn, 1, 1)) || '-' ||
                 hex(substr(GuidColumn, 6, 1)) ||
                 hex(substr(GuidColumn, 5, 1)) || '-' ||
                 hex(substr(GuidColumn, 8, 1)) ||
                 hex(substr(GuidColumn, 7, 1)) || '-' ||
                 hex(substr(GuidColumn, 9, 2)) || '-' ||
                 hex(substr(GuidColumn, 11, 6))
WHERE typeof(GuidColumn) == 'blob';

No EF Core, você também pode continuar usando o comportamento anterior configurando um conversor de valor nessas propriedades.

modelBuilder
    .Entity<MyEntity>()
    .Property(e => e.GuidProperty)
    .HasConversion(
        g => g.ToByteArray(),
        b => new Guid(b));

Microsoft.Data.Sqlite permanece capaz de efetuar a leitura de valores Guid a partir de ambas as colunas BLOB e TEXT; no entanto, como o formato padrão para parâmetros e constantes foi alterado, provavelmente será necessário tomar medidas para a maioria dos cenários que envolvem Guids.

Os valores Char agora são armazenados como TEXT no SQLite

Problema de rastreamento #15020

Comportamento antigo

Os valores Char foram previamente armazenados como valores INTEGER no SQLite. Por exemplo, um valor char de A foi armazenado como o valor inteiro 65.

Novo comportamento

Os valores Char agora são armazenados como TEXT.

Porquê

Armazenar os valores como TEXTO é mais natural e torna o banco de dados mais compatível com outras tecnologias.

Atenuações

Você pode migrar bancos de dados existentes para o novo formato executando SQL da seguinte forma.

UPDATE MyTable
SET CharColumn = char(CharColumn)
WHERE typeof(CharColumn) = 'integer';

No EF Core, você também pode continuar usando o comportamento anterior configurando um conversor de valor nessas propriedades.

modelBuilder
    .Entity<MyEntity>()
    .Property(e => e.CharProperty)
    .HasConversion(
        c => (long)c,
        i => (char)i);

Microsoft.Data.Sqlite também permanece capaz de ler valores de caracteres das colunas INTEGER e TEXT, portanto, determinados cenários podem não exigir nenhuma ação.

Os IDs de migração agora são gerados usando o calendário da cultura invariante

Tracking Issue #12978

Comportamento antigo

Os IDs de migração foram gerados inadvertidamente usando o calendário da cultura atual.

Novo comportamento

Os IDs de migração agora são sempre gerados usando o calendário da cultura invariante (gregoriano).

Porquê

A ordem das migrações é importante ao atualizar o banco de dados ou resolver conflitos de mesclagem. Usar o calendário invariante evita problemas de ordenação que podem resultar de membros da equipe com calendários do sistema diferentes.

Atenuações

Esta mudança afeta qualquer pessoa que use um calendário não-gregoriano onde o ano é maior do que o calendário gregoriano (como o calendário budista tailandês). As IDs de migração existentes precisarão ser atualizadas para que novas migrações sejam ordenadas após as migrações existentes.

A ID de migração pode ser encontrada no atributo Migration nos arquivos de designer das migrações.

 [DbContext(typeof(MyDbContext))]
-[Migration("25620318122820_MyMigration")]
+[Migration("20190318122820_MyMigration")]
 partial class MyMigration
 {

A tabela Histórico de migrações também precisa ser atualizada.

UPDATE __EFMigrationsHistory
SET MigrationId = CONCAT(LEFT(MigrationId, 4)  - 543, SUBSTRING(MigrationId, 4, 150))

UseRowNumberForPaging foi removido

Questão de Acompanhamento #16400

Comportamento antigo

Antes do EF Core 3.0, UseRowNumberForPaging podia ser usado para gerar SQL para paginação compatível com o SQL Server 2008.

Novo comportamento

A partir do EF Core 3.0, o EF só gerará SQL para paginação que só seja compatível com versões posteriores do SQL Server.

Porquê

Estamos fazendo essa alteração porque SQL Server 2008 não é mais um produto com suporte e atualizar esse recurso para trabalhar com as alterações de consulta feitas no EF Core 3.0 é um trabalho significativo.

Atenuações

Recomendamos atualizar para uma versão mais recente do SQL Server ou usar um nível de compatibilidade mais alto, para que o SQL gerado seja suportado. Dito isto, se você não puder fazer isso, por favor, comente sobre o problema de rastreamento com detalhes. Podemos rever esta decisão com base no feedback.

As informações/metadados da extensão foram removidos de IDbContextOptionsExtension

Issue de Acompanhamento #16119

Comportamento antigo

IDbContextOptionsExtension continha métodos para fornecer metadados sobre a extensão.

Novo comportamento

Esses métodos foram movidos para uma nova classe base abstrata DbContextOptionsExtensionInfo, que é retornada de uma nova propriedade IDbContextOptionsExtension.Info.

Porquê

Ao longo das versões de 2.0 a 3.0, precisávamos adicionar ou alterar esses métodos várias vezes. Dividi-los em uma nova classe base abstrata tornará mais fácil fazer esse tipo de alterações sem quebrar as extensões existentes.

Atenuações

Atualize as extensões para seguir o novo padrão. Exemplos são encontrados nas muitas implementações de IDbContextOptionsExtension para diferentes tipos de extensões no código-fonte do EF Core.

O LogQueryPossibleExceptionWithAggregateOperator foi renomeado

Tracking Issue #10985

Alteração

RelationalEventId.LogQueryPossibleExceptionWithAggregateOperator foi renomeado para RelationalEventId.LogQueryPossibleExceptionWithAggregateOperatorWarning.

Porquê

Alinha a nomenclatura desse evento de aviso com todos os outros eventos de aviso.

Atenuações

Use o novo nome. (Observe que o número de ID do evento não foi alterado.)

Esclarecer a API relativamente aos nomes de restrição de chave estrangeira

Problema de rastreamento #10730

Comportamento antigo

Antes do EF Core 3.0, os nomes de restrição de chave estrangeira eram chamados simplesmente de "nome". Por exemplo:

var constraintName = myForeignKey.Name;

Novo comportamento

A partir do EF Core 3.0, os nomes de restrição de chave estrangeira agora são chamados de "nome da restrição". Por exemplo:

var constraintName = myForeignKey.ConstraintName;

Porquê

Essa alteração traz consistência à nomenclatura nessa área e também esclarece que esse é o nome da restrição de chave estrangeira e não o nome da coluna ou propriedade na qual a chave estrangeira está definida.

Atenuações

Use o novo nome.

IRelationalDatabaseCreator.HasTables/HasTablesAsync foram tornados públicos

Questão de Acompanhamento #15997

Comportamento antigo

Antes do EF Core 3.0, esses métodos eram protegidos.

Novo comportamento

A partir do EF Core 3.0, esses métodos são públicos.

Porquê

Esses métodos são usados pelo EF para determinar se um banco de dados é criado, mas vazio. Isso também pode ser útil de fora do EF ao determinar se as migrações devem ou não ser aplicadas.

Atenuações

Altere a acessibilidade de quaisquer substituições.

Microsoft.EntityFrameworkCore.Design agora é um pacote DevelopmentDependency

Tracking Issue #11506

Comportamento antigo

Antes do EF Core 3.0, Microsoft.EntityFrameworkCore.Design era um pacote NuGet regular cujo assembly podia ser referenciado por projetos que dependiam dele.

Novo comportamento

Começando com o EF Core 3.0, é um pacote DevelopmentDependency. Isso significa que a dependência não será propagada para outros projetos de forma transitiva e que você, por padrão, já não poderá referenciar diretamente a sua assembly.

Porquê

Este pacote destina-se apenas a ser usado na fase de design. Os aplicativos implantados não devem fazer referência a ele. Tornar o pacote uma dependência de desenvolvimento reforça esta recomendação.

Atenuações

Se precisar referenciar este pacote para substituir o comportamento na fase de design do EF Core, pode atualizar os metadados do item PackageReference no seu projeto.

<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.0.0">
  <PrivateAssets>all</PrivateAssets>
  <!-- Remove IncludeAssets to allow compiling against the assembly -->
  <!--<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
</PackageReference>

Se o pacote estiver sendo referenciado transitivamente via Microsoft.EntityFrameworkCore.Tools, você precisará adicionar um PackageReference explícito ao pacote para alterar seus metadados. Essa referência explícita deve ser adicionada a qualquer projeto em que os tipos do pacote sejam necessários.

SQLitePCL.raw atualizado para a versão 2.0.0

Tracking Issue #14824

Comportamento antigo

Microsoft.EntityFrameworkCore.Sqlite dependia anteriormente da versão 1.1.12 do SQLitePCL.raw.

Novo comportamento

Atualizámos o nosso pacote para depender da versão 2.0.0.

Porquê

A versão 2.0.0 do SQLitePCL.raw destina-se ao .NET Standard 2.0. Anteriormente, ele tinha como alvo o .NET Standard 1.1, que exigia um grande fechamento de pacotes transitivos para funcionar.

Atenuações

SQLitePCL.raw versão 2.0.0 inclui algumas alterações significativas. Consulte as notas de versão para obter detalhes.

NetTopologySuite atualizado para a versão 2.0.0

Tracking Issue #14825

Comportamento antigo

Os pacotes espaciais dependiam anteriormente da versão 1.15.1 do NetTopologySuite.

Novo comportamento

Atualizamos nosso pacote para depender da versão 2.0.0.

Porquê

A versão 2.0.0 do NetTopologySuite visa resolver vários problemas de usabilidade encontrados pelos usuários do EF Core.

Atenuações

NetTopologySuite versão 2.0.0 inclui algumas alterações de quebra. Consulte as notas de versão para obter detalhes.

Microsoft.Data.SqlClient é usado em vez de System.Data.SqlClient

Tracking Issue #15636

Comportamento antigo

Microsoft.EntityFrameworkCore.SqlServer dependia anteriormente de System.Data.SqlClient.

Novo comportamento

Atualizamos nosso pacote para depender de Microsoft.Data.SqlClient.

Porquê

Microsoft.Data.SqlClient é o principal driver de acesso a dados para o SQL Server no futuro, e System.Data.SqlClient não será mais o foco do desenvolvimento. Alguns recursos importantes, como Always Encrypted, só estão disponíveis em Microsoft.Data.SqlClient.

Atenuações

Se seu código tem uma dependência direta de System.Data.SqlClient, você deve alterá-lo para fazer referência a Microsoft.Data.SqlClient em vez disso; como os dois pacotes mantêm um grau muito alto de compatibilidade de API, isso deve ser apenas uma simples alteração de pacote e namespace.

Várias relações ambíguas de autorreferência devem ser configuradas

Tracking Issue #13573

Comportamento antigo

Um tipo de entidade com várias propriedades de navegação unidirecional de autorreferência e FKs correspondentes foi configurado incorretamente como uma única relação. Por exemplo:

public class User
{
        public Guid Id { get; set; }
        public User CreatedBy { get; set; }
        public User UpdatedBy { get; set; }
        public Guid CreatedById { get; set; }
        public Guid? UpdatedById { get; set; }
}

Novo comportamento

Esse cenário agora é detetado na construção do modelo e uma exceção é lançada, indicando que o modelo é ambíguo.

Porquê

O modelo resultante era ambíguo e provavelmente estará errado para este caso.

Atenuações

Utilize a configuração completa da relação. Por exemplo:

modelBuilder
     .Entity<User>()
     .HasOne(e => e.CreatedBy)
     .WithMany();

 modelBuilder
     .Entity<User>()
     .HasOne(e => e.UpdatedBy)
     .WithMany();

DbFunction.Schema sendo nulo ou string vazia configura-o para que esteja no esquema padrão do modelo

Tracking Issue #12757

Comportamento antigo

Um DbFunction configurado com esquema como uma cadeia de caracteres vazia foi tratado como função interna sem um esquema. Por exemplo, o código a seguir mapeará a função CLR DatePart para a função interna DATEPART no SQL Server.

[DbFunction("DATEPART", Schema = "")]
public static int? DatePart(string datePartArg, DateTime? date) => throw new Exception();

Novo comportamento

Todos os mapeamentos DbFunction são considerados mapeados para funções definidas pelo usuário. Portanto, o valor da cadeia de caracteres vazia colocaria a função dentro do esquema padrão para o modelo. O esquema pode ser configurado explicitamente via a API fluente modelBuilder.HasDefaultSchema() ou, de outra forma, dbo.

Porquê

Anteriormente, o facto de o esquema estar vazio era uma maneira de indicar que a função em questão era incorporada, mas essa lógica só é aplicável ao SqlServer, onde as funções incorporadas não pertencem a nenhum esquema.

Atenuações

Configure manualmente a tradução de DbFunction para mapeá-la para uma função interna.

modelBuilder
    .HasDbFunction(typeof(MyContext).GetMethod(nameof(MyContext.DatePart)))
    .HasTranslation(args => SqlFunctionExpression.Create("DatePart", args, typeof(int?), null));

EF Core 3.0 tem como alvo o .NET Standard 2.1 em vez de .NET Standard 2.0 Reverted

Tracking Issue #15498

O EF Core 3.0 tem como alvo o .NET Standard 2.1, que é uma alteração significativa que exclui aplicativos do .NET Framework. O EF Core 3.1 reverteu isso e passou a direcionar .NET Standard 2.0 novamente.

A execução da consulta está a ser registada no nível de depuração revertida

Tracking Issue #14523

Revertemos essa alteração porque a nova configuração no EF Core 3.0 permite que o nível de log para qualquer evento seja especificado pelo aplicativo. Por exemplo, para mudar o registo de SQL para Debug, configure explicitamente o nível em OnConfiguring ou AddDbContext.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseSqlServer(connectionString)
        .ConfigureWarnings(c => c.Log((RelationalEventId.CommandExecuting, LogLevel.Debug)));