Alterações significativas incluídas no EF Core 3.x
As seguintes alterações de API e comportamento têm o potencial de interromper aplicativos existentes ao atualizá-los para 3.x. As alterações que esperamos que afetem apenas os provedores de banco de dados estão documentadas em alterações de provedor.
Resumo
Alterações de alto impacto
As consultas LINQ não são mais avaliadas no cliente
Acompanhamento de problema nº 14935Confira também o problema nº 12795
Comportamento antigo
Antes da 3.0, quando o EF Core não podia converter uma expressão que fazia parte de uma consulta para SQL ou parâmetro, ela automaticamente avaliava a expressão no cliente. Por padrão, a avaliação do cliente de expressões potencialmente dispendiosas apenas disparava um aviso.
Novo comportamento
A partir da 3.0, o EF Core só permite expressões na projeção de nível superior (a última chamada Select()
na consulta) a ser avaliada no cliente.
Quando expressões em qualquer outra parte da consulta não podem ser convertidas em SQL ou parâmetro, uma exceção é lançada.
Por que
A avaliação automática de consultas do cliente permite que várias consultas sejam executadas, mesmo que partes importantes delas não possam ser convertidas.
Esse comportamento pode resultar em comportamento inesperado e potencialmente perigoso que pode se tornar evidente apenas em produção.
Por exemplo, uma condição em uma chamada Where()
que não pode ser convertida pode fazer todas as linhas da tabela serem transferidas do servidor de banco de dados e o filtro ser aplicado no cliente.
Essa situação pode facilmente passar despercebidas se a tabela contém apenas algumas linhas no desenvolvimento, mas ter consequências sérias quando o aplicativo passa para a produção, em que a tabela pode conter milhões de linhas.
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 cliente pode levar a problemas em que melhorar a conversão da consulta para expressões específicas causa alterações significativas não intencionais entre versões.
Atenuações
Se não for possível converter totalmente uma consulta, reescreva a consulta em uma forma que possa ser convertida ou use AsEnumerable()
, ToList()
ou semelhante para explicitamente trazer dados de volta para o cliente em um local em que possam ser processados usando o 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
Anúncios de acompanhamento de problema nº 325
Comportamento antigo
Antes do ASP.NET Core 3.0, quando você adicionava uma referência de pacote a Microsoft.AspNetCore.App
ou Microsoft.AspNetCore.All
, ela incluiria o EF Core e alguns provedores de dados do EF Core, como o provedor do SQL Server.
Novo comportamento
A partir da 3.0, a estrutura compartilhada do ASP.NET Core não inclui o EF Core nem provedores de dados do EF Core.
Por que
Antes dessa alteração, obter o EF Core exigia diferentes etapas, dependendo de se o aplicativo tem como destino o ASP.NET Core e o SQL Server ou não. Além disso, atualizar o 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 mesmo em todos os provedores, implementações de .NET com suporte e tipos de aplicativos. Os desenvolvedores agora 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 com suporte, adicione explicitamente uma referência de pacote ao provedor de banco de dados do EF Core que seu aplicativo usará.
A ferramenta de linha de comando do EF Core, dotnet ef, não faz mais parte do SDK do .NET Core
Acompanhamento de problema n. 14016
Comportamento antigo
Antes da 3.0, a ferramenta dotnet ef
foi incluída no SDK do .NET Core e ficou prontamente disponível para uso na linha de comando de qualquer projeto sem a necessidade de etapas adicionais.
Novo comportamento
Da 3.0 em diante, o SDK do .NET não inclui a ferramenta dotnet ef
, portanto, antes que você possa usá-la, você precisa instalá-la explicitamente como uma ferramenta local ou global.
Por que
Essa alteração permite distribuir e atualizar dotnet ef
como uma ferramenta de CLI do .NET regular no NuGet, consistente com o fato de que o EF Core 3.0 também é sempre distribuído como um pacote do NuGet.
Atenuações
Para conseguir gerenciar migrações ou scaffold no DbContext
, instale dotnet-ef
como uma ferramenta global:
dotnet tool install --global dotnet-ef
Você também pode obtê-lo como uma ferramenta local quando você restaura as dependências de um projeto que o declara como uma dependência de ferramentas usando um arquivo de manifesto de ferramenta.
Alterações de baixo impacto
FromSql, ExecuteSql e ExecuteSqlAsync foram renomeados
Acompanhamento de questões nº 10996
Importante
ExecuteSqlCommand
e ExecuteSqlCommandAsync
são preteridos. Em vez disso, use esses métodos.
Comportamento antigo
Em versões do EF Core anteriores à 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
Da versão 3.0 do EF Core em diante, use FromSqlRaw
, ExecuteSqlRaw
, e ExecuteSqlRawAsync
para criar uma consulta parametrizada em que os parâmetros são passados separadamente da cadeia de consulta.
Por exemplo:
context.Products.FromSqlRaw(
"SELECT * FROM Products WHERE Name = {0}",
product.Name);
Use FromSqlInterpolated
, ExecuteSqlInterpolated
, e ExecuteSqlInterpolatedAsync
para criar uma consulta parametrizada em que os parâmetros são passados como parte de uma cadeia 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.
Por que
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 para chamar o método de cadeia de caracteres interpolada e vice-versa. Isso pode resultar em consultas não parametrizadas, quando deveriam ter sido.
Atenuações
Mude para usar os novos nomes de método.
O método FromSql quando usado com o procedimento armazenado não pode ser composto
Problema de acompanhamento nº 15392
Comportamento antigo
Antes do EF Core 3.0, o método FromSql tentou detectar se o SQL passado pode ser composto. Ele fez a avaliação do cliente quando o SQL não era composá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 você estiver compondo depois de FromSqlRaw/FromSqlInterpolated, o EF Core redigirá o SQL causando a subconsulta. Portanto, se você estiver usando um procedimento armazenado com composição, obterá uma exceção para sintaxe SQL inválida.
Por que
O EF Core 3.0 não dá suporte à avaliação automática do cliente, pois ele era propenso a erros, conforme explicado aqui.
Atenuações
Se você estiver usando um procedimento armazenado em FromSqlRaw/FromSqlInterpolated, você saberá que ele não pode ser composto, portanto, 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
Acompanhamento de problema nº 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 substituíram o FromSql
) só podem ser especificados em raízes de consultas, ou seja, diretamente no DbSet<>
. A tentativa de especificá-los em qualquer outro lugar resultará em um erro de compilação.
Por que
Especificar FromSql
em qualquer lugar em vez de DbSet
não tinha nenhum significado adicional ou valor agregado e poderia causar ambiguidade em determinados cenários.
Atenuações
As invocações de FromSql
devem ser movidas para estarem diretamente no DbSet
ao qual se aplicam.
As consultas sem acompanhamento não executam mais a resolução de identidade
Problema de acompanhamento nº 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 uma ID e um tipo especificados. Isso corresponde ao comportamento das consultas de acompanhamento. Por exemplo, esta consulta:
var results = context.Products.Include(e => e.Category).AsNoTracking().ToList();
retornará a mesma instância de Category
para cada Product
associado à categoria especificada.
Novo comportamento
No EF Core 3.0 em diante, diferentes instâncias de entidade serão criadas quando uma entidade com uma ID e um tipo especificados for encontrada em locais diferentes no grafo 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.
Por que
A resolução de identidade (ou seja, determinar que uma entidade tem o mesmo tipo e a mesma ID de uma entidade encontrada anteriormente) adiciona sobrecarga extra de desempenho e memória. Isso geralmente executa um contador para descobrir o motivo pelo qual as consultas sem acompanhamento 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 são serializadas e enviadas a um cliente, o que é comum para consultas sem acompanhamento.
Atenuações
Use uma consulta de acompanhamento se a resolução de identidade for necessária.
Valores de chave temporários não estão mais definidos em instâncias de entidade
Acompanhamento de problema nº 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, esses valores temporários eram grandes números negativos.
Novo comportamento
A partir da 3.0, o EF Core armazena o valor de chave temporária como parte das informações de acompanhamento da entidade e deixa a propriedade de chave em si inalterada.
Por que
Essa alteração foi feita para impedir que valores de chave temporários erroneamente se tornem permanente quando uma entidade anteriormente controlada por alguma instância DbContext
for movida para outra instância DbContext
.
Atenuações
Aplicativos que atribuem valores de chave primária em chaves estrangeiras para formar associações entre entidades poderão depender do comportamento antigo se as chaves primárias forem geradas pelo repositório e pertencerem a entidades no estado Added
.
Isso pode ser evitado:
- Não usando chaves geradas pelo repositório.
- Definindo propriedades de navegação para formar relações, em vez de definir valores de chave estrangeira.
- Obter os valores de chave temporários reais de informações de acompanhamento de entidade.
Por exemplo,
context.Entry(blog).Property(e => e.Id).CurrentValue
retornará o valor temporário, emborablog.Id
em si não tenha sido definido.
DetectChanges respeita os valores de chave gerados pelo repositório
Acompanhamento de problema nº 14616
Comportamento antigo
Antes do EF Core 3.0, uma entidade não controlada encontrada pelo DetectChanges
seria controlada no estado Added
e inserida como uma nova linha quando SaveChanges
fosse chamado.
Novo comportamento
A partir do EF Core 3.0, se uma entidade estiver usando valores de chave gerados e algum valor de chave estiver definido, a entidade será rastreada no estado Modified
.
Isso significa que se presume que uma linha para a entidade exista e ela será atualizada quando SaveChanges
for chamado.
Se o valor da chave não for definido ou se o tipo de entidade não estiver usando as chaves geradas, a nova entidade ainda será rastreada como Added
como nas versões anteriores.
Por que
Essa alteração foi feita para tornar mais fácil e consistente trabalhar com grafos de entidade desconectados ao usar chaves geradas pelo repositório.
Atenuações
Essa alteração poderá interromper um aplicativo se um tipo de entidade estiver configurado para usar chaves geradas, mas os valores de chave estiverem explicitamente definidos para novas instâncias. A correção é configurar explicitamente as propriedades de chave para não usarem 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; }
Exclusões em cascata acontecerão imediatamente por padrão
Acompanhamento de problema nº 10114
Comportamento antigo
Antes da 3.0, as ações em cascata aplicadas do EF Core (excluindo entidades dependentes quando uma entidade de segurança obrigatória é excluída ou quando a relação com uma entidade de segurança obrigatória é interrompida) não aconteciam até que SaveChanges fosse chamado.
Novo comportamento
A partir da 3.0, o EF Core aplica ações em cascata assim que a condição de disparo é detectada.
Por exemplo, chamar context.Remove()
para excluir uma entidade principal resultará em todos dependentes obrigatórios relacionados rastreados também serem definidos como Deleted
imediatamente.
Por que
Essa alteração foi feita para melhorar a experiência em cenários de vinculação de dados e auditoria, nos quais é importante entender quais entidades serão excluídas antes de SaveChanges
ser chamado.
Atenuações
O comportamento anterior pode ser restaurado por meio das configurações em context.ChangeTracker
.
Por exemplo:
context.ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;
O carregamento ansioso de entidades relacionadas agora acontece em uma única consulta
Problema de acompanhamento nº 18022
Comportamento antigo
Antes da 3.0, carregar ansiosamente as navegações de coleção por meio Include
de operadores fazia com que várias consultas fossem geradas no banco de dados relacional, uma para cada tipo de entidade relacionada.
Novo comportamento
A partir do 3.0, o EF Core gera uma única consulta com JOINs em bancos de dados relacionais.
Por que
A emissão de várias consultas para implementar uma única consulta LINQ causou vários problemas, incluindo o desempenho negativo, pois várias viagens de ida e volta de 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 significativa, ela pode ter um efeito considerável no desempenho do aplicativo quando uma única consulta contém um grande número de operadores nas navegações de coleção Include
. Consulte este comentário para obter mais informações e para reescrever consultas de forma mais eficiente.
**
DeleteBehavior.Restrict tem uma semântica mais limpa
Acompanhamento de questões nº 12661
Comportamento antigo
Antes do 3.0, DeleteBehavior.Restrict
criava chaves estrangeiras no banco de dados com a semântica Restrict
, mas também alterava a correção interna de maneira não óbvia.
Novo comportamento
No 3.0 em diante, DeleteBehavior.Restrict
garante que as chaves estrangeiras são criadas com a semântica Restrict
– ou seja, nenhuma cascata é gerada em violação de restrição – sem afetar também a correção interna do EF.
Por que
Essa alteração foi feita para melhorar a experiência do uso de DeleteBehavior
de maneira intuitiva, sem efeitos colaterais inesperados.
Atenuações
O comportamento anterior pode ser restaurado usando DeleteBehavior.ClientNoAction
.
Tipos de consulta são consolidados com tipos de entidade
Acompanhamento de problema nº 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 maneira estruturada. Ou seja, um tipo de consulta era usado para mapear os tipos de entidade sem chaves (mais provavelmente de uma exibição, mas possivelmente de uma tabela), enquanto um tipo de entidade normal era usado quando uma chave estava disponível (mais provavelmente de uma tabela, mas possivelmente de um modo de exibição).
Novo comportamento
Um tipo de consulta agora se torna apenas um tipo de entidade sem uma chave primária. Tipos de entidade sem chave têm a mesma funcionalidade que tipos de consulta nas versões anteriores.
Por que
Essa alteração foi feita para reduzir a confusão entre a finalidade dos tipos de consulta. Especificamente, são tipos de entidade sem chave e inerentemente somente leitura devido a isso, mas não devem ser usados apenas porque um tipo de entidade precisa ser somente leitura. Da mesma forma, geralmente são mapeados para modos de exibição, mas isso é só porque os modos de exibição geralmente não definem chaves.
Atenuações
As seguintes partes da API agora 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 será configurado por convenção para evitar erros de configuração quando uma chave primária for esperada, mas não corresponder à 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 tenham todas as propriedades definidas como null
uma null
será retornado em vez de uma entidade, se esse problema for aplicável ao seu cenário também adicionará lógica para lidar com null
os resultados.
A API de configuração para relações de tipo de propriedade mudou
Acompanhamento de problema nº 12444Acompanhamento de problema nº 9148Acompanhamento de problema nº 14153
Comportamento antigo
Antes do EF Core 3.0, a configuração da relação de propriedade era realizada diretamente após a chamada OwnsOne
ou OwnsMany
.
Novo comportamento
A partir do EF Core 3.0, agora há 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 o proprietário e a propriedade agora deve ser encadeada após WithOwner()
da mesma forma que como as outras relações são configuradas.
No entanto, a configuração para o tipo de propriedade em si ainda é 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.
Por que
Essa alteração foi feita para criar uma separação mais clara entre configurar o tipo de propriedade em si e a relação com o tipo de propriedade.
Isso, por sua vez, removerá a ambiguidade e a confusão quanto a métodos como HasForeignKey
.
Atenuações
Altere a configuração de relações de tipo de propriedade para usar a nova superfície de API, conforme mostrado no exemplo acima.
As entidades dependentes compartilham a tabela com a entidade de segurança agora são opcionais
Acompanhamento de questões nº 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; }
}
Em versões do EF Core anteriores à 3.0, se OrderDetails
fosse pertencente a Order
ou explicitamente mapeado para a mesma tabela, uma instância OrderDetails
seria sempre necessária ao adicionar um novo Order
.
Novo comportamento
Da versão 3.0 em diante, o EF Core permite adicionar um Order
sem um OrderDetails
e mapeia todas as propriedades OrderDetails
, exceto a chave primária para colunas que permitem valor nulo.
Ao realizar consultas, o EF Core definirá OrderDetails
para null
se qualquer uma de suas propriedades requeridas não tiver um valor ou se ele não tiver nenhuma propriedade necessária além da chave primária e todas as propriedades forem null
.
Atenuações
Se seu modelo tem uma tabela de compartilhamento de dependentes 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 for null
. Se isso não for possível, uma propriedade necessária deverá ser adicionada ao tipo de entidade ou pelo menos uma propriedade deverá ter um valor não null
atribuído a ela.
Todas as entidades compartilhando uma tabela com uma coluna de token de simultaneidade precisam mapeá-lo para uma propriedade
Acompanhamento de questões nº 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");
}
Em versões do EF Core anteriores à 3.0, se OrderDetails
pertencer a Order
ou for explicitamente mapeado para a mesma tabela, atualizar apenas OrderDetails
não atualizará o valor Version
no cliente e a próxima atualização falhará.
Novo comportamento
Da versão 3.0 em diante, o EF Core propaga o novo valor Version
para Order
se ele for proprietário de OrderDetails
. Caso contrário, uma exceção é gerada durante a validação do modelo.
Por que
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 precisam incluir uma propriedade que é mapeada para a coluna do token de simultaneidade. É possível criar uma no estado de sombra:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<OrderDetails>()
.Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
}
Entidades de propriedade não podem ser consultadas sem que o proprietário use uma consulta de acompanhamento
Problema de acompanhamento nº 18876
Comportamento antigo
Antes do EF Core 3.0, as entidades de propriedade podiam ser consultadas como qualquer outra navegação.
context.People.Select(p => p.Address);
Novo comportamento
A partir da 3.0, o EF Core será lançado se uma consulta de acompanhamento projetar uma entidade de propriedade sem o proprietário.
Por que
As entidades de propriedade não podem ser manipuladas sem o proprietário, portanto, na grande maioria dos casos, consultá-las dessa forma é um erro.
Atenuações
Se a entidade de propriedade deve ser controlada para ser modificada posteriormente, o proprietário deverá ser incluído na consulta.
Caso contrário, adicione uma chamada AsNoTracking()
:
context.People.Select(p => p.Address).AsNoTracking();
Agora, as propriedades herdadas de tipos não mapeados são mapeadas para uma única coluna para todos os tipos derivados
Acompanhamento de questões nº 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>();
}
Em versões do EF Core anteriores à 3.0, a propriedade ShippingAddress
seria mapeada para separar as colunas para BulkOrder
e Order
por padrão.
Novo comportamento
Da versão 3.0 em diante, o EF Core cria apenas uma coluna para ShippingAddress
.
Por que
O comportamento antigo era inesperado.
Mitigações
A propriedade ainda pode ser mapeada explicitamente para uma coluna separada sobre os 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 da propriedade de chave estrangeira não corresponde mais ao mesmo nome que a propriedade de entidade de segurança
Acompanhamento de problema nº 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
seria usada para a chave estrangeira por convenção.
No entanto, se Order
for um tipo de propriedade, isso também tornará CustomerId
a chave primária, e essa normalmente não é a expectativa.
Novo comportamento
Da versão 3.0 em diante, o EF Core não tentará usar propriedades para chaves estrangeiras por convenção se elas tiverem o mesmo nome que a propriedade de entidade de segurança. Nome do tipo de entidade de segurança concatenado com o nome da propriedade de entidade de segurança e nome de navegação concatenado com padrões de nome de propriedade de entidade de segurança ainda são correspondidos. 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; }
}
Por que
Essa alteração foi feita para evitar definir erroneamente uma propriedade de chave primária no tipo de propriedade.
Atenuações
Caso a propriedade se destine a ser a chave estrangeira e, portanto, parte da chave primária, configure-a explicitamente como tal.
A conexão de banco de dados agora será fechada se não for mais usada antes da conclusão do TransactionScope
Acompanhamento de questões nº 14218
Comportamento antigo
Em versões do EF Core anteriores à 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());
context.SaveChanges();
// Old behavior: Connection is still open at this point
var categories = context.ProductCategories().ToList();
}
}
Novo comportamento
Da versão 3.0 em diante, o EF Core fecha a conexão assim que termina de usá-la.
Por que
Essa alteração permite para usar vários contextos no mesmo TransactionScope
. O novo comportamento também corresponde ao EF6.
Atenuações
Se a conexão precisar permanecer aberta, uma chamada explícita para OpenConnection()
garantirá que o EF Core não a feche prematuramente:
using (new TransactionScope())
{
using (AdventureWorks context = new AdventureWorks())
{
context.Database.OpenConnection();
context.ProductCategories.Add(new ProductCategory());
context.SaveChanges();
var categories = context.ProductCategories().ToList();
context.Database.CloseConnection();
}
}
Cada propriedade usa geração de chave de inteiro em memória independente
Acompanhamento de problema nº 6872
Comportamento antigo
Antes do EF Core 3.0, um gerador de valor compartilhado era usado para todas as propriedades de chave de inteiro em memória.
Novo comportamento
Cada propriedade de chave de inteiro a partir do EF Core 3.0 obtém seu próprio gerador de valor ao usar o banco de dados em memória. Além disso, se o banco de dados for excluído, a geração de chave será redefinida para todas as tabelas.
Por que
Essa alteração foi feita para alinhar melhor a geração de chave em memória com a geração de chave do banco de dados real e para melhorar a capacidade de isolar testes uns dos outros ao usar o banco de dados em memória.
Atenuações
Isso pode interromper um aplicativo que depende da definição de valores específicos de chave em memória. Considere, em vez disso, não depender de valores de chave específicos ou atualizar para coincidir com o novo comportamento.
Campos de suporte são usados por padrão
Acompanhamento de problema nº 12430
Comportamento antigo
Antes da 3.0, mesmo que o campo de suporte para uma propriedade fosse conhecido, o EF Core ainda faria, por padrão, a leitura e a gravação do valor da propriedade usando os métodos getter e setter da propriedade. A exceção a isso era a execução da consulta, em que o campo de suporte seria definido diretamente, se fosse conhecido.
Novo comportamento
Do EF Core 3.0 em diante, se o campo de suporte para uma propriedade é conhecido, o EF Core sempre lê e grava essa propriedade usando o campo de suporte. Isso poderá causar uma interrupção de aplicativo se o aplicativo depender do comportamento adicional codificado para os métodos getter ou setter.
Por que
Essa alteração foi feita para impedir que o EF Core erroneamente acionasse a lógica de negócios por padrão, ao executar operações de banco de dados envolvendo as entidades.
Atenuações
O comportamento anterior a 3.0 pode ser restaurado pela configuração do modo de acesso da propriedade em ModelBuilder
.
Por exemplo:
modelBuilder.UsePropertyAccessMode(PropertyAccessMode.PreferFieldDuringConstruction);
Lançado se vários campos de suporte compatíveis forem encontrados
Acompanhamento de problema nº 12523
Comportamento antigo
Antes do EF Core 3.0, se vários campos correspondessem às regras para localizar o campo de suporte de uma propriedade, então um campo seria escolhido com base em uma ordem de precedência. Isso pode fazer o campo errado ser usado em casos ambíguos.
Novo comportamento
A partir do EF Core 3.0, se vários campos corresponderem à mesma propriedade, uma exceção será lançada.
Por que
Essa alteração foi feita para evitar usar silenciosamente um campo em detrimento de outro, quando apenas um puder ser correto.
Atenuações
As propriedades com campos de suporte ambíguos devem ter o campo a ser usado explicitamente especificado. Por exemplo, usando a API fluente:
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.HasField("_id");
Os nomes de propriedade somente de campo devem corresponder ao nome de campo
Comportamento antigo
Antes do EF Core 3.0, uma propriedade poderia 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
No EF Core 3.0 em diante, uma propriedade somente de campo deve corresponder exatamente ao nome de campo.
modelBuilder
.Entity<Blog>()
.Property("_id");
Por que
Essa alteração foi feita para evitar o uso do mesmo campo para duas propriedades nomeadas de forma semelhante; também torna as regras de correspondência para propriedades somente de campo as mesmas propriedades mapeadas para propriedades CLR.
Atenuações
As propriedades somente de campo devem ter o mesmo nome do campo para o qual são mapeadas. Em uma versão futura do EF Core após a 3.0, planejamos reabilitar explicitamente a configuração de um nome de campo diferente do nome da propriedade (consulte o problema nº 15307):
modelBuilder
.Entity<Blog>()
.Property("Id")
.HasField("_id");
AddDbContext/AddDbContextPool não chama mais AddLogging e AddMemoryCache
Acompanhamento de problema nº 14756
Comportamento antigo
Antes do EF Core 3.0, chamar AddDbContext
ou AddDbContextPool
também registrar serviços de cache de memória e registro em log com DI por meio 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 DI (injeção de dependência).
Por que
O EF Core 3.0 não requer que esses serviços estejam no contêiner de DI do aplicativo. No entanto, se ILoggerFactory
estiver registrado no contêiner de DI do aplicativo, ele ainda será usado pelo EF Core.
Atenuações
Se seu aplicativo precisar desses serviços, registre-os explicitamente com o contêiner de DI usando AddLogging ou AddMemoryCache.
AddEntityFramework* adiciona IMemoryCache com um limite de tamanho
Problema de acompanhamento nº 12905
Comportamento antigo
Antes do EF Core 3.0, os métodos de chamada AddEntityFramework*
também registravam 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 outros serviços adicionados posteriormente dependerem de IMemoryCache, eles poderão atingir rapidamente o limite padrão, causando exceções ou desempenho degradado.
Por que
O uso de IMemoryCache sem um limite poderá resultar no uso de memória descontrolada se houver um bug na lógica de cache de consulta ou se as consultas forem geradas dinamicamente. Ter um limite padrão reduz um possível ataque do DoS.
Atenuações
Na maioria dos casos, a chamada AddEntityFramework*
não é necessária se AddDbContext
ou AddDbContextPool
é chamada também. Portanto, a melhor mitigação é remover a AddEntityFramework*
chamada.
Se o aplicativo precisar desses serviços, registre uma implementação IMemoryCache explicitamente com o contêiner de DI com antecedência usando AddMemoryCache.
DbContext.Entry agora executa uma DetectChanges local
Acompanhamento de problema nº 13552
Comportamento antigo
Antes do EF Core 3.0, chamar DbContext.Entry
faria alterações serem detectadas para todas as entidades rastreadas.
Isso garantia que o estado exposto em EntityEntry
fosse atualizado.
Novo comportamento
A partir do EF Core 3.0, chamar DbContext.Entry
agora tentará apenas detectar alterações na entidade determinada e quaisquer entidades principais rastreadas relacionadas a ela.
Isso significa que as alterações em outro lugar talvez não tenham sido detectadas chamando esse método, o que podia ter implicações no estado do aplicativo.
Observe que, se ChangeTracker.AutoDetectChangesEnabled
for definido como false
, até mesmo essa detecção de alteração local será desabilitada.
Outros métodos que causam detecção de alteração – por exemplo ChangeTracker.Entries
e SaveChanges
– ainda causam uma DetectChanges
completa de todas as entidades de controladas.
Por que
Essa alteração foi feita para melhorar o desempenho padrão de usar context.Entry
.
Atenuações
Chame ChangeTracker.DetectChanges()
explicitamente antes de chamar Entry
para garantir o comportamento pré-3.0.
As chaves de matriz de byte e cadeia de caracteres não são geradas pelo cliente por padrão
Acompanhamento de problema nº 14617
Comportamento antigo
Antes do EF Core 3.0, as propriedades string
e byte[]
podiam ser usadas sem configurar explicitamente um valor não nulo.
Nesse caso, o valor da chave será gerado no cliente como um GUID, serializado em bytes para byte[]
.
Novo comportamento
A partir do EF Core 3.0, uma exceção será gerada indicando que nenhum valor de chave foi definido.
Por que
Essa alteração foi feita porque os valores string
/byte[]
gerados pelo cliente costumam não ser úteis, e o comportamento padrão tornava difícil refletir 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 chave devem usar valores gerados caso nenhum outro valor não nulo seja 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
Acompanhamento de problema nº 14698
Comportamento antigo
Antes do EF Core 3.0, ILoggerFactory
era registrado como um serviço singleton.
Novo comportamento
A partir do EF Core 3.0, ILoggerFactory
agora está registrado no escopo.
Por que
Essa alteração foi feita para permitir a associação de um agente com uma instância DbContext
, que habilita outra funcionalidade e remove alguns casos de comportamento patológico como uma explosão de provedores de serviço interno.
Atenuações
Essa alteração não deverá afetar o código do aplicativo, a menos que esteja registrando e usando os serviços personalizados no provedor de serviço interno do EF Core.
Isso não é comum.
Nesses casos, a maioria das coisas ainda funcionará, mas qualquer serviço singleton que dependa de ILoggerFactory
precisará ser alterado para obter o ILoggerFactory
de maneira diferente.
Se você enfrentar situações como essa, registre um problema no Rastreador de problemas do GitHub do EF Core para informar como você está usando ILoggerFactory
para que possamos entender melhor como não interromper isso novamente no futuro.
Proxies de carregamento lento não presumem mais que as propriedades de navegação estejam totalmente carregadas
Acompanhamento de problema nº 12780
Comportamento antigo
Antes do EF Core 3.0, quando DbContext
era descartado, não havia como saber se uma propriedade de navegação fornecida em uma entidade obtida daquele contexto estava totalmente carregada ou não.
Os proxies, em vez disso, presumirão que uma navegação de referência foi carregada caso ela tenha um valor não nulo e que uma navegação de coleção foi carregada caso ela não esteja vazia.
Nesses casos, a tentativa de realizar um carregamento lento seria inoperante.
Novo comportamento
A partir do EF Core 3.0, proxies controlam de se uma propriedade de navegação é carregada ou não. Isso significa tentar acessar uma propriedade de navegação carregada após o contexto ter sido descartado sempre será não operacional, mesmo quando a navegação carregada for nula ou vazia. Por outro lado, a tentativa de acessar uma propriedade de navegação que não está carregada gerará 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 ocorre, isso significa que o código do aplicativo está tentando usar o carregamento lento em uma hora inválida e o aplicativo deve ser alterado para não fazer isso.
Por que
Essa alteração foi feita para tornar o comportamento consistente e correto ao tentar realizar um carregamento lento em uma instância DbContext
descartada.
Atenuações
Atualize o código do aplicativo para não tentar carregamento lento com um contexto descartado ou configure-o para ser inoperante, conforme descrito na mensagem de exceção.
Criação excessiva de provedores de serviço internos agora é um erro por padrão
Acompanhamento de problema nº 10236
Comportamento antigo
Antes do EF Core 3.0, um aviso era registrado para um aplicativo, criando um número patológico de provedores de serviço internos.
Novo comportamento
A partir do EF Core 3.0, esse aviso agora é considerado e um erro e uma exceção são lançados.
Por que
Essa alteração era feita para obter melhores códigos de aplicativo expondo esse caso patológico mais explicitamente.
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ço internos.
No entanto, o erro pode ser convertido de volta em um aviso (ou ignorado) por meio 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 chamado com uma única cadeia de caracteres
Acompanhamento de problema nº 9171
Comportamento antigo
Antes do EF Core 3.0, o código que chamava HasOne
ou HasMany
com uma única cadeia de caracteres era interpretado de forma confusa.
Por exemplo:
modelBuilder.Entity<Samurai>().HasOne("Entrance").WithOne();
O código parece estar relacionando Samurai
com algum outro tipo de entidade usando a propriedade de navegação Entrance
, que pode ser privada.
Na realidade, esse código tenta criar um relacionamento com algum tipo de entidade chamado Entrance
sem propriedade de navegação.
Novo comportamento
A partir do EF Core 3.0, o código acima agora faz o que deveria ter feito antes.
Por que
O comportamento antigo era muito confuso, especialmente ao ler o código de configuração e ao procurar erros.
Atenuações
Isso interromperá somente os aplicativos que estão explicitamente configurando relacionamentos usando cadeias de caracteres para nomes de tipos e não estão especificando a propriedade de navegação explicitamente.
Isso não é comum.
O comportamento anterior pode ser obtido explicitamente passando 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
Acompanhamento de questões nº 15184
Comportamento antigo
Os seguintes métodos assíncronos anteriormente retornavam um Task<T>
:
DbContext.FindAsync()
DbSet.FindAsync()
DbContext.AddAsync()
DbSet.AddAsync()
ValueGenerator.NextValueAsync()
(e classes derivadas)
Novo comportamento
Os métodos mencionados anteriormente agora retornam um ValueTask<T>
sobre o mesmo T
que antes.
Por que
Essa alteração reduz o número de alocações de heap incorridas ao invocar esses métodos, melhorando o desempenho geral.
Atenuações
Aplicativos que estão apenas aguardando as APIs acima só precisam ser recompilados. Nenhuma alteração de fonte é necessária.
Um uso mais complexo (por exemplo, passando o Task
retornado a Task.WhenAny()
) normalmente exigem que o ValueTask<T>
retornado seja convertido em um Task<T>
chamando AsTask()
nele.
Observe que isso anula a redução de alocação feita por essa alteração.
A anotação Relational:TypeMapping agora é apenas TypeMapping
Acompanhamento de problema nº 9913
Comportamento antigo
O nome da anotação para anotações de mapeamento de tipo era "Relational:TypeMapping".
Novo comportamento
O nome de anotação para anotações de mapeamento de tipo agora é "TypeMapping".
Por que
Mapeamentos de tipo agora são usados mais do que apenas para provedores de banco de dados relacional.
Atenuações
Isso interromperá apenas aplicativos que acessam o mapeamento de tipo diretamente como uma anotação, o que não é comum. A ação mais apropriada para a correção é usar a superfície de API para acessar mapeamentos de tipo, em vez de usar a anotação diretamente.
ToTable em um tipo derivado gera uma exceção
Acompanhamento de problema nº 11811
Comportamento antigo
Antes do EF Core 3.0, ToTable()
chamado em um tipo derivado era ignorado, uma vez que a única estratégia de mapeamento de herança era TPH, em que isso não é válido.
Novo comportamento
A partir do EF Core 3.0 e em preparação para a adição de suporte a TPT e TPC em uma versão posterior, ToTable()
chamado em um tipo derivado agora gerará uma exceção para evitar uma alteração de mapeamento inesperada no futuro.
Por que
Atualmente, não é válido mapear um tipo derivado para uma tabela diferente. Essa alteração evita interrupção no futuro, quando se torna algo válido a se fazer.
Atenuações
Remova quaisquer tentativas de mapear tipos derivados para outras tabelas.
ForSqlServerHasIndex substituído por HasIndex
Acompanhamento de problema nº 12366
Comportamento antigo
Antes do EF Core 3.0, ForSqlServerHasIndex().ForSqlServerInclude()
fornecia uma forma de configurar as colunas usadas com INCLUDE
.
Novo comportamento
A partir do EF Core 3.0, agora há suporte para usar Include
em um índice no nível relacional.
Use HasIndex().ForSqlServerInclude()
.
Por que
Essa alteração foi feita para consolidar a API para índices com Include
em um local para todos os provedores de banco de dados.
Atenuações
Use a nova API, conforme mostrado acima.
Alterações na API de metadados
Acompanhamento de questões nº 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()
Por que
Essa alteração simplifica a implementação das interfaces mencionados anteriormente.
Atenuações
Use os novos métodos de extensão.
Alterações na API de metadados específicos do provedor
Acompanhamento de questões nº 214
Novo comportamento
Os métodos de extensão específicos do provedor serão nivelados:
IProperty.Relational().ColumnName
->IProperty.GetColumnName()
IEntityType.SqlServer().IsMemoryOptimized
->IEntityType.IsMemoryOptimized()
PropertyBuilder.UseSqlServerIdentityColumn()
->PropertyBuilder.UseIdentityColumn()
Por que
Essa alteração simplifica a implementação dos métodos de extensão mencionados anteriormente.
Atenuações
Use os novos métodos de extensão.
O EF Core não envia mais pragma para imposição do FK SQLite
Acompanhamento de problema nº 12151
Comportamento antigo
Antes do EF Core 3.0, o EF Core enviava PRAGMA foreign_keys = 1
quando uma conexão para o SQLite era 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 para o SQLite é aberta.
Por que
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 é ativada por padrão e não precisa ser habilitada explicitamente sempre que uma conexão é aberta.
Atenuações
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, chaves estrangeiras podem ser habilitadas especificando Foreign Keys=True
em sua 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 era usado SQLitePCLRaw.bundle_green
.
Novo comportamento
A partir do EF Core 3.0, o EF Core usa SQLitePCLRaw.bundle_e_sqlite3
.
Por que
Essa alteração foi feita de modo que a versão do SQLite usada no iOS fosse consistente com outras plataformas.
Atenuações
Para usar a versão do SQLite nativa no iOS, configure Microsoft.Data.Sqlite
para usar um pacote SQLitePCLRaw
diferente.
Os valores de Guid agora são armazenados como TEXTO no SQLite
Acompanhamento de problema nº 15078
Comportamento antigo
Os valores de Guid foram previamente armazenados como valores de BLOB no SQLite.
Novo comportamento
Agora os valores de Guid são armazenados como TEXTO.
Por que
O formato binário de Guids não é padronizado. O armazenamento dos valores como TEXTO permite que o banco de dados seja mais compatível com outras tecnologias.
Atenuações
Você pode migrar os bancos de dados existentes para o novo formato executando um código SQL semelhante ao seguinte.
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ê 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));
O Microsoft.Data.Sqlite ainda pode ler valores de Guid das colunas BLOB e TEXTO; no entanto, como o formato padrão para parâmetros e constantes foi alterado, é provável que você precise tomar medidas para a maioria dos cenários que envolvem Guids.
Os valores de char agora são armazenados como TEXTO no SQLite
Problema de acompanhamento nº 15020
Comportamento antigo
Anteriormente, os valores de char eram armazenados como valores INTEIROS no SQLite. Por exemplo, o valor de char A era armazenado como o valor inteiro 65.
Novo comportamento
Agora, os valores de char são armazenados como TEXTO.
Por que
O armazenamento dos valores como TEXTO é mais natural e permite que o banco de dados seja mais compatível com outras tecnologias.
Atenuações
Você pode migrar os bancos de dados existentes para o novo formato executando um código SQL semelhante ao seguinte.
UPDATE MyTable
SET CharColumn = char(CharColumn)
WHERE typeof(CharColumn) = 'integer';
No EF Core, você 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);
O Microsoft.Data.Sqlite também continua capaz de ler os valores de caractere das colunas de INTEIRO e de TEXTO, para que determinados cenários não exijam nenhuma ação.
As IDs de migração agora são geradas usando o calendário da cultura invariável
Problema de acompanhamento nº 12978
Comportamento antigo
As IDs de migração eram geradas inadvertidamente, usando o calendário da cultura atual.
Novo comportamento
Agora, as IDs de migração sempre são geradas usando o calendário da cultura invariável (gregoriano).
Por que
A ordem das migrações é importante para a atualização do banco de dados ou a resolução de conflitos de mesclagem. O uso do calendário invariável evita os problemas de ordenação que podem ocorrer quando os membros da equipe têm calendários de sistemas diferentes.
Atenuações
Essa alteração afeta todos aqueles que usam um calendário não gregoriano no qual o ano é maior do que o do calendário gregoriano (como o calendário budista tailandês). As IDs de migração existentes precisarão ser atualizadas para que as novas migrações sejam ordenadas após as migrações existentes.
A ID da migração pode ser encontrada no atributo Migração nos arquivos de designer das migrações.
[DbContext(typeof(MyDbContext))]
-[Migration("25620318122820_MyMigration")]
+[Migration("20190318122820_MyMigration")]
partial class MyMigration
{
A tabela de 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
Acompanhamento de problema nº 16400
Comportamento antigo
Antes do EF Core 3.0, o UseRowNumberForPaging
podia ser usado para gerar SQL para paginação compatível com SQL Server 2008.
Novo comportamento
A partir do EF Core 3.0, o EF só gerará SQL para paginação que seja compatível apenas com as versões posteriores do SQL Server.
Por que
Estamos fazendo essa alteração porque o SQL Server 2008 não é mais um produto com suporte e a atualização desse recurso para trabalhar com as alterações de consulta feitas no EF Core 3.0 gera um trabalho significativo.
Atenuações
É recomendável atualizar para uma versão mais recente do SQL Server ou usar um nível de compatibilidade superior para que haja suporte para o SQL gerado. Assim, se isso não for possível, comente no rastreamento do problema com detalhes. Poderemos rever essa decisão com base nos comentários.
As informações de extensão/metadados foram removidas do IDbContextOptionsExtension
Acompanhamento de problema nº 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 da nova propriedade IDbContextOptionsExtension.Info
.
Por que
Nas versões 2.0 a 3.0, precisamos adicionar ou alterar esses métodos várias vezes. Dividi-los em uma nova classe base abstrata facilitará a realização desse tipo de alteração sem interromper as extensões existentes.
Atenuações
Atualize as extensões para que sigam o novo padrão.
Os exemplos são encontrados em várias implementações de IDbContextOptionsExtension
em diferentes tipos de extensões no código-fonte do EF Core.
LogQueryPossibleExceptionWithAggregateOperator foi renomeado
Acompanhamento de problema nº 10985
Alterar
RelationalEventId.LogQueryPossibleExceptionWithAggregateOperator
foi renomeado para RelationalEventId.LogQueryPossibleExceptionWithAggregateOperatorWarning
.
Por que
Alinha a nomeação deste evento de aviso com todos os outros eventos de aviso.
Atenuações
Use o novo nome. (Observe que o número da ID do evento não foi alterado.)
Esclarecer a API para nomes de restrição de chave estrangeira
Acompanhamento de problema nº 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 são chamados de "nome de restrição". Por exemplo:
var constraintName = myForeignKey.ConstraintName;
Por que
Essa alteração traz consistência à nomeação nessa área e também esclarece que esse é o nome de restrição de chave estrangeira, e não o nome da coluna ou da propriedade na qual a chave estrangeira está definida.
Atenuações
Use o novo nome.
IRelationalDatabaseCreator.HasTables/HasTablesAsync tornaram-se públicos
Acompanhamento de problema nº 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.
Por que
Esses métodos são usados pelo EF para determinar se um banco de dados foi criado, mas está vazio. Isso também pode ser útil fora do EF ao determinar se as migrações serão ou não aplicadas.
Atenuações
Altere a acessibilidade de todas as substituições.
Agora Microsoft.EntityFrameworkCore.Design é um pacote de DevelopmentDependency
Acompanhamento de problema nº 11506
Comportamento antigo
Antes do EF Core 3.0, o Microsoft.EntityFrameworkCore.Design era um pacote regular do NuGet cujo assembly podia ser referenciado por projetos que dependiam dele.
Novo comportamento
A partir do EF Core 3.0, ele se tornou um pacote de DevelopmentDependency. Isso significa que a dependência não fluirá transitivamente para outros projetos e que você não pode mais, por padrão, referenciar seu assembly.
Por que
Esse pacote destina-se para uso somente em tempo de design. Os aplicativos implantados não devem fazer referência a ele. Tornar o pacote um DevelopmentDependency reforça essa recomendação.
Atenuações
Se você precisar fazer referência a esse pacote para substituir o comportamento de tempo de design do EF Core, atualize os metadados do item PackageReference em 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 por meio de 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
Acompanhamento de problema nº 14824
Comportamento antigo
Anteriormente, Microsoft.EntityFrameworkCore.Sqlite dependia da versão 1.1.12 do SQLitePCL.raw.
Novo comportamento
Atualizamos nosso pacote para depender da versão 2.0.0.
Por que
A versão 2.0.0 do SQLitePCL.raw é voltada para o .NET Standard 2.0. Anteriormente, era voltada ao .NET Standard 1.1, o que exigia um fechamento grande de pacotes transitivos para funcionar.
Atenuações
O SQLitePCL.raw versão 2.0.0 inclui algumas alterações de falha. Confira as notas sobre a versão para obter mais detalhes.
NetTopologySuite atualizado para a versão 2.0.0
Acompanhamento do problema n.º 14825
Comportamento antigo
Os pacotes espaciais anteriormente dependiam da versão 1.15.1 do NetTopologySuite.
Novo comportamento
Atualizamos nosso pacote para depender da versão 2.0.0.
Por que
O objetivo da versão 2.0.0 do NetTopologySuite é abordar vários problemas de usabilidade encontrados pelos usuários do EF Core.
Atenuações
O NetTopologySuite versão 2.0.0 inclui algumas alterações significativas. Confira as notas sobre a versão para obter mais detalhes.
Microsoft.Data.SqlClient é usado em vez de System.Data.SqlClient
Problema de acompanhamento nº 15636
Comportamento antigo
Microsoft.EntityFrameworkCore.SqlServer anteriormente dependia de System.Data.SqlClient.
Novo comportamento
Atualizamos nosso pacote para depender de Microsoft.Data.SqlClient.
Por que
Microsoft.Data.SqlClient é o principal driver de acesso a dados do SQL Server no futuro e System.Data.SqlClient não é mais o foco do desenvolvimento. Alguns recursos importantes, como o Always Encrypted, só estão disponíveis em Microsoft.Data.SqlClient.
Atenuações
Se o código usa uma dependência direta em System.Data.SqlClient, você deve alterá-lo para fazer referência a Microsoft.Data.SqlClient; como os dois pacotes mantêm um grau muito alto de compatibilidade de API, isso deve ser apenas um pacote simples e uma alteração de namespace.
Várias relações ambíguas de autorreferência devem ser configuradas
Acompanhamento de Problema nº 13573
Comportamento antigo
Um tipo de entidade com várias propriedades de navegação unidirecionais de autorreferência e correspondência de FKs foi configurado incorretamente como uma relação única. 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 é detectado na criação de modelo e uma exceção é gerada indicando que o modelo é ambíguo.
Por que
O modelo resultante era ambíguo e provavelmente, em geral, estará errado para esse caso.
Atenuações
Use 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 cadeia de caracteres vazia configura-o para estar no esquema padrão do modelo
Problema de acompanhamento nº 12757
Comportamento antigo
Um DbFunction configurado com o esquema como uma cadeia de caracteres vazia foi tratado como uma função interna sem um esquema. Por exemplo, o código a seguir mapeará DatePart
a função CLR para a DATEPART
função interna no SqlServer.
[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 de cadeia de caracteres vazio colocaria a função dentro do esquema padrão do modelo. Que pode ser o esquema configurado explicitamente por meio da API modelBuilder.HasDefaultSchema()
fluente ou dbo
de outra forma.
Por que
Anteriormente, o esquema estava vazio era uma maneira de tratar que a função é interna, mas essa lógica só é aplicável ao SqlServer em que as funções internas não pertencem a nenhum esquema.
Atenuações
Configure a tradução de DbFunction manualmente 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));
O EF Core 3.0 tem como destino o .NET Standard 2.1 em vez do .NET Standard 2.0 Revertido
Problema de acompanhamento nº 15498
O EF Core 3.0 tem como destino o .NET Standard 2.1, que é uma alteração significativa que exclui aplicativos do .NET Framework. O EF Core 3.1 reverteu isso e direciona o .NET Standard 2.0 novamente.
A execução de consulta é registrada no nível da Depuração Revertida
Acompanhamento de problema nº 14523
Revertemos essa alteração porque a nova configuração do EF Core 3.0 permite que o nível de log de qualquer evento seja especificado pelo aplicativo. Por exemplo, para alternar o registro em log do 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)));