Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Esta página documenta alterações de API e comportamento que podem afetar aplicações existentes ao atualizar do EF Core 6 para o EF Core 7. Certifique-se de revisar as alterações disruptivas anteriores se estiver a atualizar a partir de uma versão anterior do EF Core.
Plataforma de Destino
O EF Core 7.0 tem como alvo o .NET 6. Isto significa que as aplicações existentes que têm como objetivo .NET 6 podem continuar a fazê-lo. Aplicações direcionadas a versões antigas de .NET, .NET Core e .NET Framework terão de apontar para .NET 6 ou .NET 7 para usar o EF Core 7.0.
Resumo
Alterações de alto impacto
Encrypt é configurado por defeito como true para ligações ao SQL Server
Problema de rastreamento: SqlClient #1210
Importante
Isto representa uma mudança severa e disruptiva no pacote Microsoft.Data.SqlClient . Não há nada que possa ser feito no EF Core para reverter ou mitigar esta alteração. Por favor, envie o seu feedback para o Microsoft Data.SqlClient GitHub Repo ou contacte um Profissional de Suporte da Microsoft para questões adicionais ou ajuda.
Comportamento antigo
As cadeias de ligação SqlClient utilizam Encrypt=False por defeito. Isto permite ligações em máquinas de desenvolvimento onde o servidor local não tem um certificado válido.
Novo comportamento
As cadeias de ligação SqlClient usam Encrypt=True por defeito. Isto significa que:
- O servidor deve estar configurado com um certificado válido
- O cliente deve confiar neste certificado
Se estas condições não forem cumpridas, então um SqlException será lançado. Por exemplo:
Uma conexão foi estabelecida com êxito com o servidor, mas ocorreu um erro durante o processo de login. (provedor: Provedor SSL, erro: 0 - A cadeia de certificados foi emitida por uma autoridade não confiável.)
Porquê
Esta alteração foi feita para garantir que, por defeito, ou a ligação é segura ou a aplicação não conseguirá ligar.
Atenuações
Existem três formas de proceder:
- Instala um certificado válido no servidor. Note que este é um processo complexo e requer a obtenção de um certificado e a garantia de que é assinado por uma autoridade de confiança para o cliente.
- Se o servidor tiver um certificado, mas este não for confiável pelo cliente, então
TrustServerCertificate=Truedeve permitir contornar o mecanismo normal de confiança. - Adiciona
Encrypt=Falseexplicitamente à cadeia de ligação.
Advertência
As opções 2 e 3 deixam o servidor em um estado potencialmente inseguro.
Alguns avisos lançam exceções por padrão novamente
Problema de Rastreamento #29069
Comportamento antigo
No EF Core 6.0, um bug no provedor do SQL Server resultou em que alguns avisos configurados para, por defeito, lançar exceções estavam a ser registados, mas não estavam a lançar exceções como pretendido. Estes avisos são:
| ID do Evento | Description |
|---|---|
| RelationalEventId.AmbientTransactionWarning | Uma aplicação pode ter esperado que uma transação ambiente fosse usada quando na verdade foi ignorada. |
| RelationalEventId.IndexPropertiesBothMappedAndNotMappedToTable | Um índice especifica propriedades, algumas das quais estão mapeadas para uma coluna numa tabela, enquanto outras não estão. |
| RelationalEventId.IndexPropertiesMappedToNonOverlappingTables | Um índice especifica propriedades que correspondem a colunas em tabelas não sobrepostas. |
| RelationalEventId.ForeignKeyPropertiesMappedToUnrelatedTables | Uma chave estrangeira especifica propriedades que não correspondem às tabelas relacionadas. |
Novo comportamento
A partir do EF Core 7.0, estes avisos voltam a resultar, por defeito, numa exceção lançada.
Porquê
Estes são problemas que muito provavelmente indicam um erro no código da aplicação que deveria ser corrigido.
Atenuações
Corrigir o problema subjacente que é a razão do aviso.
Alternativamente, o nível de alarme pode ser alterado para que seja apenas registado ou completamente suprimido. Por exemplo:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.ConfigureWarnings(b => b.Ignore(RelationalEventId.AmbientTransactionWarning));
Tabelas do SQL Server com triggers ou certas colunas calculadas exigem agora uma configuração especial do EF Core.
Comportamento antigo
Versões anteriores do fornecedor SQL Server guardavam as alterações através de uma técnica menos eficiente que funcionava sempre.
Novo comportamento
Por padrão, o EF Core agora guarda alterações através de uma técnica significativamente mais eficiente; infelizmente, esta técnica não é suportada no SQL Server se a tabela de destino tiver triggers de base de dados ou certos tipos de colunas computadas. Consulte a documentação do SQL Server para mais detalhes.
Porquê
As melhorias de desempenho associadas ao novo método são suficientemente significativas para que seja importante disponibilizá-las aos utilizadores por padrão. Ao mesmo tempo, estimamos que o uso dos gatilhos da base de dados ou das colunas calculadas afetadas nas aplicações EF Core seja suficientemente baixo para que as consequências negativas das alterações disruptivas sejam superadas pelo ganho de desempenho.
Atenuações
No EF7 ou posterior, se a tabela de alvo tiver um trigger, podes informar o EF Core disso, e o EF voltará à técnica anterior, menos eficiente. Isto pode ser feito configurando o tipo de entidade correspondente da seguinte forma:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable(tb => tb.UseSqlOutputClause(false));
}
Para mais informações, incluindo como configurar isto para todas as suas tabelas, consulte a documentação do SQL Server.
As tabelas SQLite com triggers AFTER e tabelas virtuais agora requerem uma configuração especial do EF Core
Problema de Rastreamento #29916
Comportamento antigo
Versões anteriores do fornecedor SQLite guardavam as alterações através de uma técnica menos eficiente que funcionava sempre.
Novo comportamento
Por padrão, o EF Core agora guarda alterações através de uma técnica mais eficiente de guardar alterações, usando a cláusula RETURNING. Infelizmente, esta técnica não é suportada no SQLite se a tabela alvo tiver triggers AFTER da base de dados, for virtual, ou se estiverem a ser usadas versões mais antigas do SQLite. Consulte a documentação do SQLite para mais detalhes.
Porquê
As simplificações e melhorias de desempenho associadas ao novo método são suficientemente significativas para que seja importante disponibilizá-las automaticamente aos utilizadores. Ao mesmo tempo, estimamos que o uso de gatilhos de base de dados e tabelas virtuais em aplicações EF Core seja suficientemente baixo, de modo que o impacto negativo das alterações que quebram a compatibilidade seja superado pelo ganho de desempenho.
Atenuações
No EF Core 8.0, o UseSqlReturningClause método foi introduzido para reverter explicitamente ao SQL mais antigo e menos eficiente. Por exemplo:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable(tb => tb.UseSqlReturningClause(false));
}
Se ainda estiveres a usar o EF Core 7.0, então é possível reverter ao mecanismo antigo para toda a aplicação inserindo o seguinte código na tua configuração de contexto:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlite(...)
.ReplaceService<IUpdateSqlGenerator, SqliteLegacyUpdateSqlGenerator>();
Alterações de impacto médio
Dependentes órfãos de relações opcionais não são automaticamente eliminados
Problema de Rastreamento #27217
Comportamento antigo
Uma relação é opcional se a sua chave estrangeira for anulável. Definir a chave estrangeira como nula permite que a entidade dependente exista sem qualquer entidade principal relacionada. Relações opcionais podem ser configuradas para usar eliminações em cascata, embora isso não seja o padrão.
Um dependente opcional pode ser separado do seu principal, quer definindo a sua chave estrangeira como nula, quer limpando a navegação para ou a partir dela. No EF Core 6.0, isto fazia com que o dependente fosse eliminado quando a relação era configurada para a eliminação em cascata.
Novo comportamento
A partir do EF Core 7.0, o dependente deixa de ser eliminado. Note que, se o principal for eliminado, o dependente continuará a ser eliminado, uma vez que as eliminações em cascata estão configuradas para a relação.
Porquê
O dependente pode existir sem qualquer relação com um principal, pelo que a ruptura da relação não deve causar a eliminação da entidade.
Atenuações
O dependente pode ser explicitamente eliminado:
context.Remove(blog);
Ou SaveChanges pode ser substituído ou interceptado para eliminar dependentes sem referência principal. Por exemplo:
context.SavingChanges += (c, _) =>
{
foreach (var entry in ((DbContext)c!).ChangeTracker
.Entries<Blog>()
.Where(e => e.State == EntityState.Modified))
{
if (entry.Reference(e => e.Author).CurrentValue == null)
{
entry.State = EntityState.Deleted;
}
}
};
A eliminação em cascata é configurada entre tabelas ao usar o mapeamento TPT com SQL Server
Problema de Rastreamento #28532
Comportamento antigo
Ao mapear uma hierarquia de herança usando a estratégia TPT, a tabela base deve conter uma linha para cada entidade guardada, independentemente do tipo real dessa entidade. Eliminar a linha na tabela base deve apagar linhas em todas as outras tabelas. O EF Core configura uma eliminação em cascata para isto.
No EF Core 6.0, um bug no fornecedor de base de dados SQL Server significava que estas eliminações em cascata não estavam a ser criadas.
Novo comportamento
A partir do EF Core 7.0, as eliminações em cascata estão agora a ser criadas para o SQL Server, tal como sempre acontecia com outras bases de dados.
Porquê
Eliminações em cascata da tabela base para as sub-tabelas no TPT permitem que uma entidade seja eliminada eliminando a sua linha na tabela base.
Atenuações
Na maioria dos casos, esta alteração não deverá causar problemas. No entanto, o SQL Server é muito restritivo quando existem múltiplos comportamentos em cascata configurados entre tabelas. Isto significa que, se existir uma relação em cascata existente entre tabelas no mapeamento TPT, então o SQL Server pode gerar o seguinte erro:
Microsoft.Data.SqlClient.SqlException: A instrução DELETE entrou em conflito com a restrição REFERENCE "FK_Blogs_People_OwnerId". O conflito ocorreu na base de dados "Scratch", tabela "dbo. Blogs", coluna 'OwnerId'. A instrução foi terminada.
Por exemplo, este modelo cria um ciclo de relações em cascata:
[Table("FeaturedPosts")]
public class FeaturedPost : Post
{
public int ReferencePostId { get; set; }
public Post ReferencePost { get; set; } = null!;
}
[Table("Posts")]
public class Post
{
public int Id { get; set; }
public string? Title { get; set; }
public string? Content { get; set; }
}
Um destes terá de ser configurado para não usar eliminações em cascata no servidor. Por exemplo, para alterar a relação explícita:
modelBuilder
.Entity<FeaturedPost>()
.HasOne(e => e.ReferencePost)
.WithMany()
.OnDelete(DeleteBehavior.ClientCascade);
Ou para alterar a relação implícita criada para o mapeamento TPT:
modelBuilder
.Entity<FeaturedPost>()
.HasOne<Post>()
.WithOne()
.HasForeignKey<FeaturedPost>(e => e.Id)
.OnDelete(DeleteBehavior.ClientCascade);
Maior probabilidade de erros ocupados/bloqueados no SQLite quando não se usa registo de write-ahead
Comportamento antigo
Versões anteriores do fornecedor SQLite armazenavam alterações utilizando uma técnica menos eficiente, que permitia tentativas automáticas quando a tabela estava bloqueada ou ocupada e o registo antecipado de escrita (WAL) não estava ativado.
Novo comportamento
Por padrão, o EF Core agora guarda alterações através de uma técnica mais eficiente, usando a cláusula RETURNING. Infelizmente, esta técnica não consegue tentar automaticamente quando está ocupado/bloqueado. Numa aplicação multithreaded (como uma aplicação web) que não utiliza write-ahead logging (registo antecipado), é comum encontrar estes erros.
Porquê
As simplificações e melhorias de desempenho associadas ao novo método são suficientemente significativas para que seja importante disponibilizá-las automaticamente aos utilizadores. Bases de dados criadas pelo EF Core também ativam o write-ahead logging por defeito. A equipa do SQLite também recomenda a ativação do registo de escrita antecipada por padrão.
Atenuações
Se possível, deve ativar o registo de escrita antecipada na sua base de dados. Se a tua base de dados foi criada pela EF, isso já deveria ser o caso. Se não, pode ativar o registo de escrita antecipada executando o seguinte comando.
PRAGMA journal_mode = 'wal';
Se, por algum motivo, não conseguir ativar o registo de escrita antecipada, é possível reverter ao mecanismo antigo de toda a aplicação inserindo o seguinte código na configuração do seu contexto:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlite(...)
.ReplaceService<IUpdateSqlGenerator, SqliteLegacyUpdateSqlGenerator>();
Alterações de baixo impacto
As propriedades-chave podem precisar de ser configuradas com um comparador de valor do fornecedor
Problema de Rastreamento #27738
Comportamento antigo
No EF Core 6.0, os valores-chave retirados diretamente das propriedades dos tipos de entidade eram usados para comparação dos valores-chave ao guardar alterações. Isto faria uso de qualquer comparador de valor personalizado configurado nestas propriedades.
Novo comportamento
A partir do EF Core 7.0, os valores da base de dados são usados para estas comparações. Isto "simplesmente funciona" na grande maioria dos casos. No entanto, se as propriedades estivessem a usar um comparador personalizado, e esse comparador não pudesse ser aplicado aos valores da base de dados, então pode ser necessário um "comparador de valor fornecedor", como mostrado abaixo.
Porquê
Várias divisões de entidades e tabelas podem resultar em múltiplas propriedades mapeadas para a mesma coluna da base de dados, e vice-versa. Isto exige que os valores sejam comparados após a conversão para valores que serão usados na base de dados.
Atenuações
Configure um comparador de valor do fornecedor. Por exemplo, considere o caso em que um objeto valor está a ser usado como chave, e o comparador dessa chave usa comparações de cadeias insensíveis a maiúsculas e minúsculas:
var blogKeyComparer = new ValueComparer<BlogKey>(
(l, r) => string.Equals(l.Id, r.Id, StringComparison.OrdinalIgnoreCase),
v => v.Id.ToUpper().GetHashCode(),
v => v);
var blogKeyConverter = new ValueConverter<BlogKey, string>(
v => v.Id,
v => new BlogKey(v));
modelBuilder.Entity<Blog>()
.Property(e => e.Id).HasConversion(
blogKeyConverter, blogKeyComparer);
Os valores (cadeias) da base de dados não podem usar diretamente o comparador definido para tipos BlogKey. Portanto, um comparador fornecedor para comparações de cadeias insensíveis a maiúsculas minúsculas deve ser configurado:
var caseInsensitiveComparer = new ValueComparer<string>(
(l, r) => string.Equals(l, r, StringComparison.OrdinalIgnoreCase),
v => v.ToUpper().GetHashCode(),
v => v);
var blogKeyComparer = new ValueComparer<BlogKey>(
(l, r) => string.Equals(l.Id, r.Id, StringComparison.OrdinalIgnoreCase),
v => v.Id.ToUpper().GetHashCode(),
v => v);
var blogKeyConverter = new ValueConverter<BlogKey, string>(
v => v.Id,
v => new BlogKey(v));
modelBuilder.Entity<Blog>()
.Property(e => e.Id).HasConversion(
blogKeyConverter, blogKeyComparer, caseInsensitiveComparer);
Restrições de verificação e outras características da tabela estão agora configuradas na tabela
Comportamento antigo
No EF Core 6.0, HasCheckConstraint, HasComment, e IsMemoryOptimized eram chamados diretamente no construtor de tipos de entidade. Por exemplo:
modelBuilder
.Entity<Blog>()
.HasCheckConstraint("CK_Blog_TooFewBits", "Id > 1023");
modelBuilder
.Entity<Blog>()
.HasComment("It's my table, and I'll delete it if I want to.");
modelBuilder
.Entity<Blog>()
.IsMemoryOptimized();
Novo comportamento
A partir do EF Core 7.0, estes métodos passam a ser chamados no construtor de tabelas:
modelBuilder
.Entity<Blog>()
.ToTable(b => b.HasCheckConstraint("CK_Blog_TooFewBits", "Id > 1023"));
modelBuilder
.Entity<Blog>()
.ToTable(b => b.HasComment("It's my table, and I'll delete it if I want to."));
modelBuilder
.Entity<Blog>()
.ToTable(b => b.IsMemoryOptimized());
Os métodos existentes foram marcados como Obsolete. Atualmente, têm o mesmo comportamento dos novos métodos, mas serão removidos numa versão futura.
Porquê
Estes aspetos aplicam-se apenas às tabelas. Não serão aplicadas a vistas mapeadas, funções ou procedimentos armazenados.
Atenuações
Use os métodos do construtor de tabelas, como mostrado acima.
As navegações de novas entidades para entidades eliminadas não são corrigidas
Problema de Rastreamento #28249
Comportamento antigo
No EF Core 6.0, quando uma nova entidade é rastreada, seja a partir de uma consulta de rastreamento ou ao anexá-la ao DbContext, as navegações para e a partir de entidades relacionadas no Deleted estado são corrigidas.
Novo comportamento
A partir do EF Core 7.0, as navegações de e para entidades Deleted não são ajustadas.
Porquê
Depois de uma entidade ser marcada como Deleted, raramente faz sentido associá-la a entidades não eliminadas.
Atenuações
Consulte ou anexe entidades antes de marcar as entidades como Deleted, ou defina manualmente as propriedades de navegação para e a partir da entidade eliminada.
Usar FromSqlRaw e métodos relacionados do fornecedor errado gera um erro de tipo usar-o-método-correto
Comportamento antigo
No EF Core 6.0, usar o método de extensão Azure Cosmos DB FromSqlRaw ao usar um fornecedor relacional, ou o método de extensão relacional FromSqlRaw ao usar o fornecedor Azure Cosmos DB pode falhar silenciosamente. Da mesma forma, usar métodos relacionais no fornecedor em memória é um no-opsilencioso.
Novo comportamento
A partir do EF Core 7.0, usar um método de extensão concebido para um provedor em outro provedor lançará uma exceção.
Porquê
O método correto de extensão deve ser usado para que funcione corretamente em todas as situações.
Atenuações
Use o método correto de extensão para o fornecedor utilizado. Se vários fornecedores forem referenciados, então chama o método de extensão como um método estático. Por exemplo:
var result = await CosmosQueryableExtensions.FromSqlRaw(context.Blogs, "SELECT ...").ToListAsync();
Ou:
var result = await RelationalQueryableExtensions.FromSqlRaw(context.Blogs, "SELECT ...").ToListAsync();
O andaime OnConfiguring deixa de chamar IsConfigured
Comportamento antigo
No EF Core 6.0, o DbContext tipo formado a partir de uma base de dados existente continha uma chamada para IsConfigured. Por exemplo:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
optionsBuilder.UseNpgsql("MySecretConnectionString");
}
}
Novo comportamento
A partir do EF Core 7.0, a chamada para IsConfigured já não está incluída.
Porquê
Existem cenários muito limitados em que o fornecedor da base de dados está configurado dentro do seu DbContext em alguns casos, mas apenas se o contexto ainda não estiver configurado. Em vez disso, manter OnConfiguring aqui torna mais provável que uma cadeia de ligação contendo dados sensíveis permaneça no código, apesar do aviso em tempo de compilação. Assim, o código mais seguro e limpo resultante da remoção disto foi considerado válido, especialmente tendo em conta que a flag --no-onconfiguring (.NET CLI) ou -NoOnConfiguring (Visual Studio Package Manager Console) pode ser usada para evitar a geração do método OnConfiguring, e que existem modelos personalizáveis para adicionar de volta IsConfigured se for realmente necessário.
Atenuações
Ou então
- Use o argumento
--no-onconfiguring(.NET CLI) ou-NoOnConfiguring(Console do Gestor de Pacotes do Visual Studio) ao usar andaimes a partir de uma base de dados existente. -
Personalize os modelos T4 para adicionar novamente a chamada a
IsConfigured.