Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questa pagina illustra le modifiche di comportamento e API che potrebbero interrompere l'aggiornamento delle applicazioni esistenti da EF Core 6 a EF Core 7. Assicurarsi di esaminare le modifiche sostanziali precedenti se si aggiorna da una versione precedente di EF Core.
Framework di destinazione
EF Core 7.0 è destinato a .NET 6. Ciò significa che le applicazioni esistenti destinate a .NET 6 possono continuare a farlo. Le applicazioni destinate a versioni precedenti di .NET, .NET Core e .NET Framework dovranno usare .NET 6 o .NET 7 per usare EF Core 7.0.
Riepilogo
Modifiche ad alto impatto
Encrypt si imposta per impostazione predefinita su true le connessioni di SQL Server
Problema di rilevamento: SqlClient #1210
Importante
Si tratta di una modifica di rilievo grave nel pacchetto Microsoft.Data.SqlClient . Non è possibile eseguire alcuna operazione in EF Core per ripristinare o attenuare questa modifica. Inviare commenti e suggerimenti al repository GitHub Microsoft.Data.SqlClient oppure contattare un supporto tecnico Microsoft Professional per ulteriori domande o assistenza.
Comportamento precedente
Una stringa di connessione SqlClient viene utilizzata per Encrypt=False impostazione predefinita. Ciò consente le connessioni nei computer di sviluppo in cui il server locale non dispone di un certificato valido.
Nuovo comportamento
Le stringhe di connessione SqlClient sono utilizzate per Encrypt=True impostazione predefinita. Ciò significa che:
- Il server deve essere configurato con un certificato valido
- Il client deve considerare attendibile questo certificato
Se queste condizioni non vengono soddisfatte, verrà generata un'eccezione SqlException . Ad esempio:
È stata stabilita una connessione con il server, ma si è verificato un errore durante il processo di accesso. (provider: provider SSL, errore: 0: la catena di certificati è stata emessa da un'autorità non attendibile)
Perché
Questa modifica è stata apportata per assicurarsi che, per impostazione predefinita, la connessione sia sicura o che l'applicazione non riesca a connettersi.
Soluzioni di prevenzione
Esistono tre modi per procedere:
- Installare un certificato valido nel server. Si noti che si tratta di un processo coinvolto e richiede di ottenere un certificato e di assicurarsi che sia firmato da un'autorità attendibile dal client.
- Se il server dispone di un certificato, ma non è considerato attendibile dal client,
TrustServerCertificate=Trueper consentire di ignorare il normale meccanismo di attendibilità. - Aggiungere
Encrypt=Falseesplicitamente alla stringa di connessione.
Avviso
Le opzioni 2 e 3 lasciano il server in uno stato potenzialmente non sicuro.
Alcuni avvisi generano nuovamente eccezioni per impostazione predefinita
Problema di rilevamento n. 29069
Comportamento precedente
In EF Core 6.0, un bug nel provider SQL Server significava che alcuni avvisi configurati per generare eccezioni per impostazione predefinita venivano registrati ma non generavano eccezioni. Questi avvisi sono:
| EventId | Descrizione |
|---|---|
| RelationalEventId.AmbientTransactionWarning | È possibile che un'applicazione abbia previsto che venga usata una transazione di ambiente quando è stata effettivamente ignorata. |
| RelationalEventId.IndexPropertiesBothMappedAndNotMappedToTable | Un indice specifica le proprietà, alcune delle quali sono mappate a una colonna nella tabella e altre no. |
| RelationalEventId.IndexPropertiesMappedToNonOverlappingTables | Un indice specifica le proprietà di cui viene eseguito il mapping alle colonne in tabelle non sovrapposte. |
| RelationalEventId.ForeignKeyPropertiesMappedToUnrelatedTables | Una chiave esterna specifica le proprietà che non eseguono il mapping alle tabelle correlate. |
Nuovo comportamento
A partire da EF Core 7.0, questi avvisi, per impostazione predefinita, generano un'eccezione.
Perché
Si tratta di problemi che indicano molto probabilmente un errore nel codice dell'applicazione che deve essere risolto.
Soluzioni di prevenzione
Consente di risolvere il problema sottostante che è il motivo dell'avviso.
In alternativa, il livello di avviso può essere modificato in modo che venga solo registrato o completamente soppresso. Ad esempio:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.ConfigureWarnings(b => b.Ignore(RelationalEventId.AmbientTransactionWarning));
Le tabelle di SQL Server con trigger o determinate colonne calcolate richiedono ora una configurazione speciale di EF Core
Problema di rilevamento n. 27372
Comportamento precedente
Le versioni precedenti del provider SQL Server hanno salvato le modifiche tramite una tecnica meno efficiente che funzionava sempre.
Nuovo comportamento
Per impostazione predefinita, EF Core salva ora le modifiche tramite una tecnica significativamente più efficiente; purtroppo questa tecnica non è supportata in SQL Server se la tabella di destinazione include trigger di database o determinati tipi di colonne calcolate. Per altri dettagli, vedere la documentazione di SQL Server.
Perché
I miglioramenti delle prestazioni collegati al nuovo metodo sono sufficientemente significativi da renderli importanti per gli utenti per impostazione predefinita. Allo stesso tempo, si stima che l'utilizzo dei trigger di database o delle colonne calcolate interessate nelle applicazioni EF Core sia sufficientemente basso da far sì che le conseguenze negative delle modifiche che interrompono la compatibilità siano superiori al miglioramento delle prestazioni.
Soluzioni di prevenzione
In EF7 o versioni successive, se la tabella di destinazione ha un trigger, è possibile informare EF Core di ciò e EF ripristinerà la tecnica precedente meno efficiente. Questa operazione può essere eseguita configurando il tipo di entità corrispondente come indicato di seguito:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable(tb => tb.UseSqlOutputClause(false));
}
Per altre informazioni, tra cui su come configurare questa opzione per tutte le tabelle, vedere la documentazione di SQL Server.
Le tabelle SQLite con trigger AFTER e tabelle virtuali richiedono ora una configurazione speciale di EF Core
Problema di rilevamento n. 29916
Comportamento precedente
Le versioni precedenti del provider SQLite hanno salvato le modifiche tramite una tecnica meno efficiente che funzionava sempre.
Nuovo comportamento
Per impostazione predefinita, EF Core salva ora le modifiche tramite una tecnica più efficiente, usando la clausola RETURNING. Sfortunatamente, questa tecnica non è supportata in SQLite se la tabella di destinazione include trigger AFTER del database, è virtuale o se vengono usate versioni precedenti di SQLite. Per altri dettagli, vedere la documentazione di SQLite.
Perché
Le semplificazioni e i miglioramenti delle prestazioni associati al nuovo metodo sono così significativi da renderli disponibili di default per gli utenti. Allo stesso tempo, stimiamo che l'utilizzo dei trigger di database e delle tabelle virtuali nelle applicazioni EF Core sia abbastanza basso da far sì che le conseguenze negative delle modifiche rompenti siano compensate dal miglioramento delle prestazioni.
Soluzioni di prevenzione
In EF Core 8.0, il metodo UseSqlReturningClause è stato introdotto per ripristinare in modo esplicito al SQL più vecchio e meno efficiente. Ad esempio:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable(tb => tb.UseSqlReturningClause(false));
}
Se si usa ancora EF Core 7.0, è possibile ripristinare il meccanismo precedente per l'intera applicazione inserendo il codice seguente nella configurazione del contesto:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlite(...)
.ReplaceService<IUpdateSqlGenerator, SqliteLegacyUpdateSqlGenerator>();
Modifiche a impatto medio
Le dipendenze orfane delle relazioni facoltative non vengono eliminate automaticamente
Problema di rilevamento n. 27217
Comportamento precedente
Una relazione è facoltativa se la chiave esterna è annullabile. L'impostazione della chiave esterna su Null consente l'esistenza dell'entità dipendente senza alcuna entità principale correlata. Le relazioni facoltative possono essere configurate per l'uso di eliminazioni a catena, anche se non è l'impostazione predefinita.
Un dipendente facoltativo può essere rimosso dalla relativa entità impostando la relativa chiave esterna su Null oppure cancellando la navigazione da o verso di essa. In EF Core 6.0, ciò causerebbe l'eliminazione del dipendente quando la relazione è stata configurata per l'eliminazione a catena.
Nuovo comportamento
A partire da EF Core 7.0, il dipendente non viene più eliminato. Si noti che se l'entità principale viene eliminata, l'entità dipendente verrà comunque eliminata perché le eliminazioni a catena sono configurate per la relazione.
Perché
La dipendenza può esistere senza alcuna relazione con un'entità principale, quindi interrompere la relazione non dovrebbe causare l'eliminazione della dipendenza.
Soluzioni di prevenzione
Il dipendente può essere eliminato in modo esplicito:
context.Remove(blog);
Oppure SaveChanges può essere sottoposto a override o intercettato per eliminare dipendenze senza riferimento principale. Ad esempio:
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;
}
}
};
L'eliminazione a catena viene configurata tra tabelle quando si usa il mapping TPT con SQL Server
Problema di rilevamento n. 28532
Comportamento precedente
Quando si esegue il mapping di una gerarchia di ereditarietà usando la strategia TPT, la tabella di base deve contenere una riga per ogni entità salvata, indipendentemente dal tipo effettivo di tale entità. L'eliminazione della riga nella tabella di base deve eliminare righe in tutte le altre tabelle. EF Core configura un'eliminazione a cascata per questo.
In EF Core 6.0, un bug nel provider di database di SQL Server significava che queste eliminazioni a catena non venivano create.
Nuovo comportamento
A partire da EF Core 7.0, le eliminazioni a catena vengono ora create per SQL Server esattamente come per gli altri database.
Perché
Le eliminazioni a catena dalla tabella di base alle tabelle secondarie in TPT consentono l'eliminazione di un'entità eliminandone la riga nella tabella di base.
Soluzioni di prevenzione
Nella maggior parte dei casi, questa modifica non dovrebbe causare problemi. Tuttavia, SQL Server è molto restrittivo quando sono configurati più comportamenti a catena tra tabelle. Ciò significa che se esiste una relazione di propagazione esistente tra le tabelle nel mapping TPT, SQL Server potrebbe generare l'errore seguente:
La Microsoft.Data.SqlClient.SqlException: l'istruzione DELETE è in conflitto con il vincolo REFERENCE "FK_Blogs_People_OwnerId". Il conflitto si è verificato nel database "Scratch", tabella "dbo. Blogs", colonna 'OwnerId'. L'istruzione è stata interrotta.
Ad esempio, questo modello crea un ciclo di relazioni a catena:
[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; }
}
Uno di questi dovrà essere configurato per non usare le eliminazioni a catena nel server. Ad esempio, per modificare la relazione esplicita:
modelBuilder
.Entity<FeaturedPost>()
.HasOne(e => e.ReferencePost)
.WithMany()
.OnDelete(DeleteBehavior.ClientCascade);
In alternativa, per modificare la relazione implicita creata per il mapping TPT:
modelBuilder
.Entity<FeaturedPost>()
.HasOne<Post>()
.WithOne()
.HasForeignKey<FeaturedPost>(e => e.Id)
.OnDelete(DeleteBehavior.ClientCascade);
Maggiore probabilità di errori occupati/bloccati in SQLite quando non si usa il logging in modalità write-ahead
Comportamento precedente
Le versioni precedenti del provider SQLite hanno salvato le modifiche tramite una tecnica meno efficiente che è stata in grado di riprovare automaticamente quando la tabella era bloccata/occupata e la registrazione write-ahead (WAL) non era abilitata.
Nuovo comportamento
Per impostazione predefinita, EF Core salva ora le modifiche tramite una tecnica più efficiente, usando la clausola RETURNING. Sfortunatamente, questa tecnica non è in grado di riprovare automaticamente quando è occupata/bloccata. In un'applicazione multithread (ad esempio un'applicazione web) che non utilizza il log scrittura anticipata, è comune riscontrare questi errori.
Perché
Le semplificazioni e i miglioramenti delle prestazioni associati al nuovo metodo sono così significativi da renderli disponibili di default per gli utenti. I database creati da EF Core abilitano di default anche il write-ahead logging. Il team di SQLite consiglia anche di abilitare la registrazione write-ahead per impostazione predefinita.
Soluzioni di prevenzione
Se possibile, è consigliabile abilitare la registrazione anticipata nel database. Se il database è stato creato da EF, questo dovrebbe già essere il caso. In caso contrario, è possibile abilitare la registrazione anticipata eseguendo il comando seguente.
PRAGMA journal_mode = 'wal';
Se, per qualche motivo, non è possibile abilitare la registrazione write-ahead, è possibile ripristinare il meccanismo precedente per l'intera applicazione inserendo il codice seguente nella configurazione del contesto:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlite(...)
.ReplaceService<IUpdateSqlGenerator, SqliteLegacyUpdateSqlGenerator>();
Modifiche a basso impatto
Potrebbe essere necessario configurare le proprietà chiave con un operatore di confronto dei valori del provider
Problema di rilevamento n. 27738
Comportamento precedente
In EF Core 6.0, i valori chiave acquisiti direttamente dalle proprietà dei tipi di entità sono stati usati per il confronto dei valori delle chiavi durante il salvataggio delle modifiche. In questo modo si userà qualsiasi operatore di confronto di valori personalizzato configurato in queste proprietà.
Nuovo comportamento
A partire da EF Core 7.0, i valori del database vengono usati per questi confronti. Questo "funziona solo" per la maggior parte dei casi. Tuttavia, se le proprietà utilizzavano un operatore di confronto personalizzato e tale operatore di confronto non può essere applicato ai valori del database, potrebbe essere necessario un "operatore di confronto dei valori del provider", come illustrato di seguito.
Perché
Varie suddivisioni di entità e di tabelle possono comportare l'attribuzione di più proprietà alla stessa colonna del database e viceversa. Questo richiede che i valori vengano confrontati dopo la conversione in valore che verrà usato nel database.
Soluzioni di prevenzione
Configurare un comparatore di valori per il provider. Si consideri ad esempio il caso in cui un oggetto valore viene usato come chiave e l'operatore di confronto per tale chiave usa confronti tra stringhe senza distinzione tra maiuscole e minuscole:
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);
I valori del database (stringhe) non possono usare direttamente l'operatore di confronto definito per BlogKey i tipi. Pertanto, è necessario configurare un comparatore di provider per confronti tra stringhe senza distinzione tra maiuscole e minuscole.
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);
I vincoli di controllo e altri aspetti della tabella sono ora configurati sulla tabella.
Problema di rilevamento n. 28205
Comportamento precedente
In EF Core 6.0, HasCheckConstraint, HasCommente IsMemoryOptimized sono stati chiamati direttamente nel generatore dei tipi di entità. Ad esempio:
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();
Nuovo comportamento
A partire da EF Core 7.0, questi metodi vengono invece chiamati nel generatore di tabelle:
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());
I metodi esistenti sono stati contrassegnati come Obsolete. Attualmente hanno lo stesso comportamento dei nuovi metodi, ma verranno rimossi in una versione futura.
Perché
Questi facet si applicano solo alle tabelle. Non verranno applicate a viste, funzioni o stored procedure mappate.
Soluzioni di prevenzione
Usare i metodi del generatore di tabelle, come illustrato in precedenza.
Gli spostamenti dalle nuove entità alle entità eliminate non vengono risolti
Problema di rilevamento n. 28249
Comportamento precedente
In EF Core 6.0, quando una nuova entità viene tracciata tramite una
Nuovo comportamento
A partire da EF Core 7.0, le navigazioni da e verso le entità Deleted non vengono fissate.
Perché
Quando un'entità viene contrassegnata come Deleted raramente ha senso associarla a entità non eliminate.
Soluzioni di prevenzione
Interrogare o allegare entità prima di contrassegnarle come Deleted, o impostare manualmente le proprietà di navigazione verso e dall'entità eliminata.
Usare FromSqlRaw e i metodi correlati del provider errato genera un'eccezione: utilizzare il metodo corretto.
Problema di rilevamento n. 26502
Comportamento precedente
In EF Core 6.0, l'uso del metodo di estensione di Azure Cosmos DB FromSqlRaw con un provider relazionale o del metodo di estensione relazionale FromSqlRaw con il provider Azure Cosmos DB potrebbe fallire senza avviso. Analogamente, l'uso di metodi relazionali nel provider in memoria è un no-op invisibile all'utente.
Nuovo comportamento
A partire da EF Core 7.0, l'uso di un metodo di estensione progettato per un provider in un provider diverso genererà un'eccezione.
Perché
Il metodo di estensione corretto deve essere usato per funzionare correttamente in tutte le situazioni.
Soluzioni di prevenzione
Usare il metodo di estensione corretto per il provider in uso. Se viene fatto riferimento a più provider, chiamare il metodo di estensione come metodo statico. Ad esempio:
var result = await CosmosQueryableExtensions.FromSqlRaw(context.Blogs, "SELECT ...").ToListAsync();
Oppure:
var result = await RelationalQueryableExtensions.FromSqlRaw(context.Blogs, "SELECT ...").ToListAsync();
Scaffolded OnConfiguring non chiama più IsConfigured
Problema di rilevamento n. 4274
Comportamento precedente
In EF Core 6.0, il tipo DbContext generato tramite scaffolding da un database esistente conteneva una chiamata a IsConfigured. Ad esempio:
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");
}
}
Nuovo comportamento
A partire da EF Core 7.0, la chiamata a IsConfigured non è più inclusa.
Perché
Esistono scenari molto limitati in cui il provider di database è configurato all'interno di DbContext in alcuni casi, ma solo se il contesto non è già configurato. Lasciando qui, invece, OnConfiguring è più probabile che un stringa di connessione contenente informazioni riservate venga lasciato nel codice, nonostante l'avviso in fase di compilazione. Di conseguenza, il codice risultante è stato giudicato più sicuro e pulito grazie alla rimozione di questo elemento, e pertanto considerato vantaggioso, soprattutto dato che l'interfaccia della riga di comando --no-onconfiguring (.NET CLI) o la console del gestione pacchetti di Visual Studio -NoOnConfiguring può essere usata per impedire lo scaffolding del metodo OnConfiguring, e che sono disponibili modelli personalizzabili per riaggiungere il metodo IsConfigured se è davvero necessario.
Soluzioni di prevenzione
Uno dei seguenti:
- Usare l'argomento
--no-onconfiguring(interfaccia della riga di comando di .NET) o-NoOnConfiguring(Console Gestione Pacchetti di Visual Studio) durante il scaffolding da un database esistente. -
Personalizzare i modelli T4 per aggiungere nuovamente la chiamata a
IsConfigured.