Breaking Changes in EF Core 6.0
Die folgenden API-Änderungen und Behavior Changes können dazu führen, dass vorhandene Anwendungen nach einem Upgrade auf EF Core 6.0 nicht mehr funktionieren.
Zielframework
EF Core 6.0 zielt auf .NET 6 ab. Anwendungen für ältere .NET-, .NET Core- und .NET Framework-Versionen müssen .NET 6 für die Verwendung von EF Core 6.0 verwenden.
Zusammenfassung
* Diese Änderungen sind für Autoren von Datenbankanbietern und -erweiterungen von besonderem Interesse.
Änderungen mit hoher Auswirkung
Geschachtelte optionale abhängige Objekte, die eine Tabelle gemeinsam nutzen und keine erforderlichen Eigenschaften aufweisen, können nicht gespeichert werden
Nachverfolgung von Issue 24558
Altes Verhalten
Modelle mit geschachtelten optionalen abhängigen Objekten, die eine Tabelle gemeinsam nutzen und keine erforderlichen Eigenschaften aufweisen, waren zulässig, konnten jedoch zu Datenverlusten führen, wenn die Daten abgefragt und dann erneut gespeichert wurden. Betrachten Sie beispielsweise das folgende Modell:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public ContactInfo ContactInfo { get; set; }
}
[Owned]
public class ContactInfo
{
public string Phone { get; set; }
public Address Address { get; set; }
}
[Owned]
public class Address
{
public string House { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
Keine der Eigenschaften in ContactInfo
oder Address
ist erforderlich, und all diese Entitätstypen werden derselben Tabelle zugeordnet. Die Regeln für optionale abhängige Objekte (im Gegensatz zu erforderlichen abhängigen Objekten) legen fest, dass beim Abfragen des Besitzers Customer
keine Instanz von ContactInfo
erstellt wird, wenn alle Spalten für ContactInfo
NULL sind. Dies bedeutet jedoch auch, dass auch dann keine Instanz von Address
erstellt wird, wenn die Address
-Spalten ungleich NULL sind.
Neues Verhalten
Wenn Sie versuchen, dieses Modell zu verwenden, wird jetzt die folgende Ausnahme ausgelöst:
System.InvalidOperationException: Der Entitätstyp „ContactInfo“ ist ein optionales abhängiges Objekt, das die Tabellenfreigabe verwendet und andere abhängige Objekte ohne erforderliche nicht freigegebene Eigenschaften enthält, um zu ermitteln, ob die Entität vorhanden ist. Wenn alle Nullwerte zulassenden Eigenschaften einen NULL-Wert in der Datenbank enthalten, wird in der Abfrage keine Objektinstanz erstellt, wodurch die Werte geschachtelter abhängiger Objekte verloren gehen. Fügen Sie eine erforderliche Eigenschaft hinzu, um Instanzen mit NULL-Werten für andere Eigenschaften zu erstellen, oder markieren Sie die eingehende Navigation als erforderlich, damit immer eine Instanz erstellt wird.
Dadurch werden Datenverluste beim Abfragen und Speichern von Daten verhindert.
Warum?
Die Verwendung von Modellen mit geschachtelten optionalen abhängigen Objekten, die eine Tabelle gemeinsam nutzen und keine erforderlichen Eigenschaften aufweisen, führten häufig zu stillschweigendem Datenverlust.
Gegenmaßnahmen
Vermeiden Sie die Verwendung optionaler abhängiger Objekte, die eine Tabelle gemeinsam nutzen und keine erforderlichen Eigenschaften aufweisen. Hierfür gibt es drei Möglichkeiten:
Legen Sie die abhängigen Objekte als erforderlich fest. Dies bedeutet, dass die abhängige Entität immer einen Wert hat, nachdem sie abgefragt wurde, auch wenn alle ihre Eigenschaften NULL sind. Beispiel:
public class Customer { public int Id { get; set; } public string Name { get; set; } [Required] public Address Address { get; set; } }
Oder:
modelBuilder.Entity<Customer>( b => { b.OwnsOne(e => e.Address); b.Navigation(e => e.Address).IsRequired(); });
Stellen Sie sicher, dass das abhängige Objekt mindestens eine erforderliche Eigenschaft enthält.
Ordnen Sie optionale abhängige Objekte einer eigenen Tabelle zu, anstatt eine Tabelle gemeinsam mit dem Prinzipal zu verwenden. Beispiel:
modelBuilder.Entity<Customer>( b => { b.ToTable("Customers"); b.OwnsOne(e => e.Address, b => b.ToTable("CustomerAddresses")); });
Die Probleme mit optionalen abhängigen Objekten und Beispiele für diese Gegenmaßnahmen sind in der Dokumentation zu Neuerungen in EF Core 6.0 enthalten.
Änderungen mit mittlerer Auswirkung
Die Änderung des Besitzers einer nicht eigenständigen Entität löst jetzt eine Ausnahme aus
Altes Verhalten
Es war möglich, eine nicht eigenständige Entität einem anderen Besitzer zuzuweisen.
Neues Verhalten
Diese Aktion löst nun eine Ausnahme aus:
The property '{entityType}.{property}' is part of a key and so cannot be modified or marked as modified. (Die Eigenschaft „{Entitätstyp}.{Eigenschaft}“ ist Teil eines Schlüssels und kann weder geändert noch als geändert gekennzeichnet werden.) Um den Prinzipal einer vorhandenen Entität mit einem identifizierenden Fremdschlüssel zu ändern, müssen Sie zuerst die abhängige Entität löschen, „SaveChanges“ aufrufen und die abhängige Entität dann mit dem neuen Prinzipal verknüpfen.
Warum?
Obwohl nicht eigenständige Typen keine Schlüsseleigenschaften aufweisen müssen, erstellt EF trotzdem Schatteneigenschaften, die als Primärschlüssel und als auf den Besitzer verweisender Fremdschlüssel verwendet werden. Wenn die besitzende Entität geändert wird, werden die Werte des Fremdschlüssels für die nicht eigenständige Entität geändert. Da sie außerderm als Primärschlüssel verwendet werden, ändert sich die Entitätsidentität. Dies wird in EF Core noch nicht vollständig unterstützt und war für nicht eigenständige Entitäten nur bedingt zulässig, was manchmal dazu führte, dass der interne Zustand inkonsistent wurde.
Gegenmaßnahmen
Anstatt die gleiche nicht eigenständige Instanz einem neuen Besitzer zuzuweisen, können Sie eine Kopie zuweisen und die alte löschen.
Azure Cosmos DB: Verwandte Entitätstypen werden als nicht eigenständig ermittelt
Nachverfolgung von Issue 24803Neuerungen: Standardmäßig impliziter Besitz
Altes Verhalten
Wie bei anderen Anbietern wurden verwandte Entitätstypen als normale (eigenständige) Typen ermittelt.
Neues Verhalten
Verwandte Entitätstypen befinden sich jetzt im Besitz des Entitätstyps, für den sie ermittelt wurden. Nur die Entitätstypen, die zu einer DbSet<TEntity>-Eigenschaft gehören, werden als eigenständige Entitätstypen ermittelt.
Warum?
Dieses Verhalten entspricht dem gängigen Muster, bei dem Daten in Azure Cosmos DB modelliert und verwandte Daten in ein einzelnes Dokument eingebettet werden. Azure Cosmos DB bietet keine native Unterstützung für das Verknüpfen verschiedener Dokumente, sodass die Modellierung verwandter Entitäten als eigenständige Entitäten nur eingeschränkt nützlich ist.
Gegenmaßnahmen
Um einen Entitätstyp so zu konfigurieren, dass er eigenständig ist, müssen Sie modelBuilder.Entity<MyEntity>();
aufrufen.
SQLite: Verbindungen werden in einem Pool zusammengefasst
Nachverfolgung von Issue 13837Neuerungen: Standardmäßig impliziter Besitz
Altes Verhalten
Bisher wurden Verbindungen in Microsoft.Data.Sqlite nicht in einem Pool zusammengefasst.
Neues Verhalten
Ab 6.0 werden Verbindungen jetzt standardmäßig in einem Pool zusammengefasst. Dies führt dazu, dass der Prozess Datenbankdateien auch nach dem Schließen des ADO.NET-Verbindungsobjekts geöffnet lässt.
Warum?
Durch das Zusammenfassen der zugrunde liegenden Verbindungen in einem Pool wird die Leistung beim Öffnen und Schließen von ADO.NET-Verbindungsobjekten erheblich verbessert. Dies ist besonders in Szenarien spürbar, in denen das Öffnen der zugrunde liegenden Verbindung kostspielig ist, wie z. B. bei der Verschlüsselung, oder in Szenarien, in denen eine große Menge kurzlebiger Verbindungen mit der Datenbank besteht.
Gegenmaßnahmen
Das Verbindungspooling kann durch Hinzufügen von Pooling=False
zu einer Verbindungszeichenfolge deaktiviert werden.
In einigen Szenarien (z. B. beim Löschen der Datenbankdatei) können jetzt Fehler auftreten, die darauf hinweisen, dass die Datei noch verwendet wird. Sie können den Verbindungspool manuell über SqliteConnection.ClearPool()
löschen, bevor Sie Vorgänge der Datei ausführen.
SqliteConnection.ClearPool(connection);
File.Delete(databaseFile);
Für m:n-Beziehungen ohne zugeordnete Joinentitäten wird jetzt ein Gerüst verwendet
Nachverfolgung von Issue 22475
Altes Verhalten
Beim Gerüstbau (Reverse Engineering) von DbContext
und Entitätstypen aus einer vorhandenen Datenbank wurden Jointabellen immer explizit zugeordnet, um Entitätstypen für m:n-Beziehungen zu verknüpfen.
Neues Verhalten
Einfache Jointabellen, die nur zwei Fremdschlüsseleigenschaften für andere Tabellen enthalten, werden nicht mehr expliziten Entitätstypen, sondern als m:n-Beziehung zwischen den beiden verknüpften Tabellen zugeordnet.
Warum?
m:n-Beziehungen ohne explizite Jointypen wurden in EF Core 5.0 eingeführt und bieten eine übersichtlichere, natürlichere Möglichkeit zur Darstellung einfacher Jointabellen.
Gegenmaßnahmen
Es gibt zwei Gegenmaßnahmen. Der bevorzugte Ansatz besteht darin, Code so zu aktualisieren, dass die m:n-Beziehungen direkt verwendet werden. Es ist sehr selten, dass der Joinentitätstyp direkt verwendet werden muss, wenn er nur zwei Fremdschlüssel für die m:n-Beziehungen enthält.
Alternativ kann die explizite Joinentität wieder dem EF-Modell hinzugefügt werden. Wenn beispielsweise eine m:n-Beziehung zwischen Post
und Tag
besteht, fügen Sie den Jointyp und die Navigationen mithilfe von partiellen Klassen wieder hinzu:
public partial class PostTag
{
public int PostsId { get; set; }
public int TagsId { get; set; }
public virtual Post Posts { get; set; }
public virtual Tag Tags { get; set; }
}
public partial class Post
{
public virtual ICollection<PostTag> PostTags { get; set; }
}
public partial class Tag
{
public virtual ICollection<PostTag> PostTags { get; set; }
}
Fügen Sie dann eine Konfiguration für den Jointyp und Navigationen einer partiellen Klasse für DbContext hinzu:
public partial class DailyContext
{
partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>(entity =>
{
entity.HasMany(d => d.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
l => l.HasOne<Tag>(e => e.Tags).WithMany(e => e.PostTags).HasForeignKey(e => e.TagsId),
r => r.HasOne<Post>(e => e.Posts).WithMany(e => e.PostTags).HasForeignKey(e => e.PostsId),
j =>
{
j.HasKey("PostsId", "TagsId");
j.ToTable("PostTag");
});
});
}
}
Entfernen Sie abschließend die generierte Konfiguration für die m:n-Beziehung aus dem Gerüstkontext. Dies ist erforderlich, weil der Joinentitätstyp des Gerüsts aus dem Modell entfernt werden muss, bevor der explizite Typ verwendet werden kann. Dieser Code muss bei jedem Gerüstbau des Kontexts entfernt werden. Da sich der obige Code jedoch in partiellen Klassen befindet, wird er beibehalten.
Beachten Sie, dass die Joinentität bei dieser Konfiguration wie in früheren Versionen von EF Core explizit verwendet werden kann. Die Beziehung kann jedoch auch als m:n-Beziehung verwendet werden. Die derartige Aktualisierung des Codes kann daher eine temporäre Lösung darstellen, während der Rest des Codes so aktualisiert wird, dass die Beziehung auf natürliche Weise als m:n verwendet wird.
Änderungen mit geringer Auswirkung
Bereinigte Zuordnung zwischen DeleteBehavior- und ON DELETE-Werten
Nachverfolgung von Issue #21252
Altes Verhalten
Einige der Zuordnungen zwischen dem OnDelete()
-Verhalten einer Beziehung und dem ON DELETE
-Verhalten von Fremdschlüsseln in der Datenbank waren in Migrationen und Gerüstbau nicht konsistent.
Neues Verhalten
In der folgenden Tabelle werden die Änderungen an Migrationen aufgeführt.
OnDelete() | ON DELETE |
---|---|
NoAction | NO ACTION |
ClientNoAction | NO ACTION |
Einschränken | RESTRICT |
Cascade | CASCADE |
ClientCascade | |
SetNull | SET NULL |
ClientSetNull |
Die Änderungen an Gerüstbau lauten wie folgt.
ON DELETE | OnDelete() |
---|---|
NO ACTION | ClientSetNull |
RESTRICT | |
CASCADE | Kaskadieren |
SET NULL | SetNull |
Warum?
Die neuen Zuordnungen sind konsistenter. Das standardmäßige Datenbankverhalten von NO ACTION wird jetzt dem restriktiveren und weniger performanten RESTRICT-Verhalten vorgezogen.
Gegenmaßnahmen
Das OnDelete()-Standardverhalten optionaler Beziehungen ist ClientSetNull. Die Zuordnung wurde von RESTRICT in NO ACTION geändert. Dies kann dazu führen, dass bei Ihrer ersten Migration viele Vorgänge generiert werden, die nach dem Upgrade auf EF Core 6.0 hinzugefügt wird.
Sie können diese Vorgänge entweder anwenden oder manuell aus der Migration entfernen, da sie keine funktionalen Auswirkungen auf EF Core haben.
SQL Server unterstützt RESTRICT nicht, diese Fremdschlüssel wurden also bereits mit NO ACTION erstellt. Die Migrationsvorgänge wirken sich nicht auf SQL Server aus und können problemlos entfernt werden.
In-Memory-Datenbank überprüft, ob erforderliche Eigenschaften NULL-Werte enthalten
Nachverfolgung von Issue 10613
Altes Verhalten
Die In-Memory-Datenbank erlaubte auch dann das Speichern von NULL-Werten, wenn die Eigenschaft wie erforderlich konfiguriert wurde.
Neues Verhalten
Die In-Memory-Datenbank löst eine Microsoft.EntityFrameworkCore.DbUpdateException
aus, wenn SaveChanges
oder SaveChangesAsync
aufgerufen wird und eine erforderliche Eigenschaft auf NULL festgelegt ist.
Warum?
Das In-Memory-Datenbankverhalten stimmt jetzt mit dem Verhalten anderer Datenbanken überein.
Gegenmaßnahmen
Das vorherige Verhalten (d. h. keine Überprüfung von NULL-Werten) kann beim Konfigurieren des In-Memory-Anbieters wiederhergestellt werden. Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseInMemoryDatabase("MyDatabase", b => b.EnableNullChecks(false));
}
Letzte ORDER BY-Anweisung beim Beitreten zu Sammlungen entfernt
Nachverfolgung von Issue #19828
Altes Verhalten
Beim Ausführen von SQL JOINs für Sammlungen (1:n-Beziehungen) wird mit EF Core für jede Schlüsselspalte der verbundenen Tabelle eine ORDER BY-Spalte hinzugefügt. Beispielsweise erfolgt ein Laden aller Blogs mit den zugehörigen Beiträgen über die folgenden SQL-Anweisung:
SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId], [p].[PostId]
Diese Anweisungen sind für die ordnungsgemäße Materialisierung der Entitäten erforderlich.
Neues Verhalten
Die letzte ORDER BY-Anweisung für eine Sammlung mit JOIN wird jetzt ausgelassen:
SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]
Es wird keine ORDER BY-Anweisung für die Spalte POST-ID mehr generiert.
Warum?
Jede ORDER BY-Anweisung bedeutet zusätzliche Arbeit auf der Datenbankseite, und die letzte Anweisung ist für die Materialisierung von EF Core nicht erforderlich. Daten zeigen, dass das Entfernen dieser letzten Anweisung in einigen Szenarios zu einer erheblichen Leistungsverbesserung führen kann.
Gegenmaßnahmen
Wenn Ihre Anwendung erwartet, dass die verbundenen Entitäten in einer bestimmten Reihenfolge zurückgegeben werden, machen Sie diese explizit, indem Sie ihrer Abfrage einen OrderBy
-Operator (LINQ) hinzufügen.
DbSet implementiert IAsyncEnumerable nicht mehr
Nachverfolgung von Issue #24041
Altes Verhalten
DbSet<TEntity> wird zum Ausführen von Abfragen für DbContext verwendet, die zum Implementieren von IAsyncEnumerable<T> verwendet werden.
Neues Verhalten
DbSet<TEntity> implementiert nicht mehr direkt IAsyncEnumerable<T>.
Warum?
DbSet<TEntity> wurde ursprünglich so gestaltet, dass hauptsächlich IAsyncEnumerable<T> implementiert wird, um eine direkte Enumeration über das Konstrukt foreach
zu ermöglichen. Wenn ein Projekt auch auf System.Linq.Async verweist, um asynchrone LINQ-Operatoren clientseitig zu erstellen, führte dies zu einem mehrdeutigen Aufruffehler zwischen den Operatoren, die über IQueryable<T>
definiert, und den Operatoren, die über IAsyncEnumerable<T>
definiert wurden. C# 9 hat die Erweiterungsunterstützung für GetEnumerator
für foreach
-Schleifen hinzugefügt, wodurch der ursprüngliche Hauptgrund für den Verweis auf IAsyncEnumerable
entfernt wird.
Die meisten Anwendungsszenarios von DbSet
funktionieren weiterhin wie gewohnt, da zum Beispiel LINQ-Operatoren über DbSet
erstellt werden oder eine Enumeration erfolgt. Die einzigen Anwendungsszenarios, die nicht funktionieren, sind diejenigen, in denen DbSet
direkt in IAsyncEnumerable
konvertiert werden soll.
Gegenmaßnahmen
Wenn Sie auf einen DbSet<TEntity> als IAsyncEnumerable<T> verweisen müssen, rufen Sie DbSet<TEntity>.AsAsyncEnumerable auf, um ihn explizit umzuwandeln.
Der Rückgabeentitätstyp einer Tabellenwertfunktion wird standardmäßig auch einer Tabelle zugeordnet
Nachverfolgung von Issue 23408
Altes Verhalten
Ein Entitätstyp wurde nicht standardmäßig einer Tabelle zugeordnet, wenn er als Rückgabetyp einer mit HasDbFunction konfigurierten Tabellenwertfunktion verwendet wurde.
Neues Verhalten
Ein Entitätstyp, der als Rückgabetyp einer Tabellenwertfunktion verwendet wird, behält die Standardtabellenzuordnung bei.
Warum?
Es ist nicht intuitiv, dass durch das Konfigurieren einer Tabellenwertfunktion die Standardtabellenzuordnung des Rückgabeentitätstyps entfernt wird.
Gegenmaßnahmen
Um die Standardtabellenzuordnung zu entfernen, müssen Sie ToTable(EntityTypeBuilder, String) aufrufen:
modelBuilder.Entity<MyEntity>().ToTable((string?)null));
Die Eindeutigkeit der Namen von Überprüfungseinschränkungen wird jetzt überprüft
Nachverfolgung von Issue 25061
Altes Verhalten
Überprüfungseinschränkungen mit demselben Namen konnten für dieselbe Tabelle deklariert und verwendet werden.
Neues Verhalten
Das explizite Konfigurieren von zwei Überprüfungseinschränkungen mit demselben Namen für die gleiche Tabelle führt jetzt zu einer Ausnahme. Überprüfungseinschränkungen, die durch eine Konvention erstellt werden, wird ein eindeutiger Name zugewiesen.
Warum?
Die meisten Datenbanken lassen nicht zu, dass zwei Überprüfungseinschränkungen mit demselben Namen für die gleiche Tabelle erstellt werden. Einige Datenbanken erfordern sogar, dass diese Namen auch tabellenübergreifend eindeutig sind. Dies würde dazu führen, dass beim Anwenden einer Migration eine Ausnahme ausgelöst wird.
Gegenmaßnahmen
In einigen Fällen können gültige Namen von Überprüfungseinschränkungen aufgrund dieser Änderung unterschiedlich sein. Um den gewünschten Namen explizit anzugeben, müssen Sie HasName aufrufen:
modelBuilder.Entity<MyEntity>().HasCheckConstraint("CK_Id", "Id > 0", c => c.HasName("CK_MyEntity_Id"));
IReadOnly-Metadatenschnittstellen wurden hinzugefügt und Erweiterungsmethoden entfernt
Nachverfolgung von Issue 19213
Altes Verhalten
Es gab drei verschiedene Metadatenschnittstellen: IModel, IMutableModel und IConventionModel sowie Erweiterungsmethoden.
Neues Verhalten
Eine neue IReadOnly
-Schnittstelle wurde hinzugefügt, z. B. IReadOnlyModel. Erweiterungsmethoden, die zuvor für die Metadatenschnittstellen definiert wurden, wurden in Standardschnittstellenmethoden konvertiert.
Warum?
Standardschnittstellenmethoden ermöglichen es, die Implementierung zu überschreiben. Dies wird von der neuen Implementierung des Laufzeitmodells genutzt, um eine bessere Leistung zu bieten.
Gegenmaßnahmen
Der Großteil des Codes sollte nicht von diesen Änderungen betroffen sein. Sollten Sie die Erweiterungsmethoden jedoch über die statische Aufrufsyntax verwenden, muss diese in Instanzaufrufsyntax konvertiert werden.
IExecutionStrategy ist jetzt ein Singletondienst
Nachverfolgung von Issue 21350
Neues Verhalten
IExecutionStrategy ist jetzt ein Singleton-Dienst. Das bedeutet, dass jeder in benutzerdefinierten Implementierungen hinzugefügte Zustand ausführungsübergreifend beibehalten und der an ExecutionStrategy übergebene Delegat nur einmal ausgeführt wird.
Warum?
Dadurch werden Zuweisungen zu zwei langsamsten Pfaden in EF reduziert.
Gegenmaßnahmen
Implementierungen, die von ExecutionStrategy ableiten, sollten jeden Zustand in OnFirstExecution() löschen.
Die bedingte Logik im Delegaten, der an ExecutionStrategy übergeben wurde, sollte in eine benutzerdefinierte Implementierung von IExecutionStrategy verschoben werden.
SQL Server: Weitere Fehler gelten als vorübergehend
Nachverfolgung von Issue 25050
Neues Verhalten
Die im obigen Issue aufgeführten Fehler gelten jetzt als vorübergehend. Wenn Sie die Standardausführungsstrategie (ohne Wiederholungen) verwenden, werden diese Fehler jetzt von einer zusätzlichen Ausnahmeinstanz umschlossen.
Warum?
Wir sammeln weiterhin Feedback von Benutzer*innen und vom SQL Server-Team dazu, welche Fehler als vorübergehend betrachtet werden sollten.
Gegenmaßnahmen
Um die Gruppe von Fehlern zu ändern, die als vorübergehend gelten, müssen Sie eine benutzerdefinierte Ausführungsstrategie einsetzen, die von SqlServerRetryingExecutionStrategy - Verbindungsresilienz – EF Core abgeleitet werden kann.
Azure Cosmos DB: Weitere Zeichen werden in id-Werten mit Escapezeichen versehen
Nachverfolgung von Issue 25100
Altes Verhalten
In EF Core 5 wurde nur '|'
in id
-Werten mit Escapezeichen versehen.
Neues Verhalten
In EF Core 6 werden auch '/'
, '\'
, '?'
und '#'
in id
-Werten mit Escapezeichen versehen.
Warum?
Diese Zeichen sind ungültig. Dies ist in Resource.Id dokumentiert. Wenn Sie sie in id
verwenden, schlagen Abfragen fehl.
Gegenmaßnahmen
Sie können den generierten Wert überschreiben, indem Sie ihn festlegen, bevor die Entität als Added
markiert wird:
var entry = context.Attach(entity);
entry.Property("__id").CurrentValue = "MyEntity|/\\?#";
entry.State = EntityState.Added;
Einige Singletondienste sind jetzt bereichsbezogen
Nachverfolgung von Issue 25084
Neues Verhalten
Viele Abfragedienste und einige Entwurfszeitdienste, die als Singleton
registriert wurden, werden jetzt als Scoped
registriert.
Warum?
Die Lebensdauer musste geändert werden, damit ein neues Feature (DefaultTypeMapping) Einfluss auf Abfragen nehmen kann.
Die Lebensdauer von Entwurfszeitdiensten wurde an die Lebensdauer von Laufzeitdiensten angepasst, um Fehler zu vermeiden, wenn beide verwendet werden.
Gegenmaßnahmen
Verwenden Sie TryAdd, um EF Core-Dienste mit der Standardlebensdauer zu registrieren. Verwenden Sie TryAddProviderSpecificServices nur für Dienste, die nicht von EF hinzugefügt werden.
Neue Zwischenspeicherungs-API für Erweiterungen, die Dienste hinzufügen oder ersetzen
Nachverfolgung von Issue 19152
Altes Verhalten
In EF Core 5 hat GetServiceProviderHashCode long
zurückgegeben und wurde direkt als Teil des Cacheschlüssels für den Dienstanbieter verwendet.
Neues Verhalten
GetServiceProviderHashCode gibt jetzt int
zurück und wird nur verwendet, um den Hashcode des Cacheschlüssels für den Dienstanbieter zu berechnen.
Außerdem muss ShouldUseSameServiceProvider implementiert werden, um anzugeben, ob das aktuelle Objekt die gleiche Dienstkonfiguration darstellt und daher denselben Dienstanbieter verwenden kann.
Warum?
Wird nur ein Hashcode als Teil des Cacheschlüssels verwendet, führte dies zu gelegentlichen Kollisionen, die schwer zu diagnostizieren und zu beheben waren. Die zusätzliche Methode stellt sicher, dass derselbe Dienstanbieter nur wenn angebracht verwendet wird.
Gegenmaßnahmen
Viele Erweiterungen machen keine Optionen verfügbar, die sich auf registrierte Dienste auswirken, und können die folgende Implementierung von ShouldUseSameServiceProvider verwenden:
private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
public ExtensionInfo(IDbContextOptionsExtension extension)
: base(extension)
{
}
...
public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
=> other is ExtensionInfo;
}
Andernfalls sollten zusätzliche Prädikate hinzugefügt werden, um alle relevanten Optionen zu vergleichen.
Neue Initialisierungsprozedur für Momentaufnahmen und Entwurfszeitmodelle
Nachverfolgung von Issue 22031
Altes Verhalten
In EF Core 5 mussten bestimmte Konventionen aufgerufen werden, bevor das Momentaufnahmemodell verwendet werden konnte.
Neues Verhalten
IModelRuntimeInitializer wurde eingeführt, um einige der erforderlichen Schritte zu verbergen, und es wurde ein Laufzeitmodell eingeführt, das nicht über alle Migrationsmetadaten verfügt. Aus diesem Grund sollte das Entwurfszeitmodell für Modellvergleiche (Diff) verwendet werden.
Warum?
IModelRuntimeInitializer abstrahiert die Modellabschlussschritte, sodass diese jetzt ohne weitere Breaking Changes für die Benutzer*innen geändert werden können.
Das optimierte Laufzeitmodell wurde eingeführt, um die Laufzeitleistung zu verbessern. Es weist mehrere Optimierungen auf. Eine Optimierung entfernt Metadaten, die zur Laufzeit nicht verwendet werden.
Gegenmaßnahmen
Der folgende Codeausschnitt veranschaulicht, wie Sie überprüfen können, ob sich das aktuelle Modell vom Momentaufnahmemodell unterscheidet:
var snapshotModel = migrationsAssembly.ModelSnapshot?.Model;
if (snapshotModel is IMutableModel mutableModel)
{
snapshotModel = mutableModel.FinalizeModel();
}
if (snapshotModel != null)
{
snapshotModel = context.GetService<IModelRuntimeInitializer>().Initialize(snapshotModel);
}
var hasDifferences = context.GetService<IMigrationsModelDiffer>().HasDifferences(
snapshotModel?.GetRelationalModel(),
context.GetService<IDesignTimeModel>().Model.GetRelationalModel());
Dieser Codeausschnitt veranschaulicht, wie Sie IDesignTimeDbContextFactory<TContext> implementieren, indem Sie ein Modell extern erstellen und UseModel aufrufen:
internal class MyDesignContext : IDesignTimeDbContextFactory<MyContext>
{
public TestContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DB"));
var modelBuilder = SqlServerConventionSetBuilder.CreateModelBuilder();
CustomizeModel(modelBuilder);
var model = modelBuilder.Model.FinalizeModel();
var serviceContext = new MyContext(optionsBuilder.Options);
model = serviceContext.GetService<IModelRuntimeInitializer>().Initialize(model);
return new MyContext(optionsBuilder.Options);
}
}
OwnedNavigationBuilder.HasIndex
gibt jetzt einen anderen Typ zurück
Nachverfolgung von Issue 24005
Altes Verhalten
In EF Core 5 gab HasIndex IndexBuilder<TEntity>
zurück, wobei TEntity
der besitzende Typ ist.
Neues Verhalten
Jetzt gibt HasIndex IndexBuilder<TDependentEntity>
zurück, wobei TDependentEntity
der besitzende Typ ist.
Warum?
Das zurückgegebene Builder-Objekt wurde nicht ordnungsgemäß typisiert.
Gegenmaßnahmen
Das Neukompilieren Ihrer Assembly für die neueste Version von EF Core reicht aus, um alle durch diese Änderung verursachten Probleme zu beheben.
DbFunctionBuilder.HasSchema(null)
überschreibt [DbFunction(Schema = "schema")]
Nachverfolgung von Issue 24228
Altes Verhalten
In EF Core 5 wurde beim Aufrufen von HasSchema mit dem Wert null
die Konfigurationsquelle nicht gespeichert, daher konnte sie von DbFunctionAttribute überschrieben werden.
Neues Verhalten
Durch Aufrufen von HasSchema mit dem Wert null
wird die Konfigurationsquelle jetzt gespeichert und verhindert, dass das Attribut sie überschreiben kann.
Warum?
Die mit der ModelBuilder-API angegebene Konfiguration sollte nicht durch Datenanmerkungen überschrieben werden können.
Gegenmaßnahmen
Entfernen Sie den HasSchema
-Aufruf, damit das Schema durch das Attribut konfiguriert werden kann.
Vorab initialisierte Navigationen werden durch Werte aus Datenbankabfragen überschrieben
Nachverfolgung von Issue 23851
Altes Verhalten
Navigationseigenschaften, die auf ein leeres Objekt festgelegt waren, wurden für Nachverfolgungsabfragen unverändert gelassen, aber für andere Abfragen überschrieben. Berücksichtigen Sie beispielsweise folgende Entitätstypen:
public class Foo
{
public int Id { get; set; }
public Bar Bar { get; set; } = new(); // Don't do this.
}
public class Bar
{
public int Id { get; set; }
}
Eine Abfrage ohne Nachverfolgung für Foo
, die Bar
einschloss, legte Foo.Bar
auf die von der Datenbank abgefragte Entität fest. Ein Beispiel ist der folgende Code:
var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Ausgabe: Foo.Bar.Id = 1
.
Dieselbe Abfrage, die für die Nachverfolgung ausgeführt wurde, hat Foo.Bar
jedoch nicht mit der aus der Datenbank abgefragten Entität überschrieben. Ein Beispiel ist der folgende Code:
var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Ausgabe: Foo.Bar.Id = 0
.
Neues Verhalten
In EF Core 6.0 entspricht das Verhalten von Nachverfolgungsabfragen jetzt dem Verhalten von Abfragen ohne Nachverfolgung. Demnach führen der Code:
var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Und dieser Code:
var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
zur Ausgabe Foo.Bar.Id = 1
.
Warum?
Es gibt zwei Gründe für diese Änderung:
- Hiermit wird sichergestellt, dass Nachverfolgungsabfragen und Abfragen ohne Nachverfolgung ein konsistentes Verhalten zeigen.
- Wenn eine Datenbank abgefragt wird, kann davon ausgegangen werden, dass der Anwendungscode die in der Datenbank gespeicherten Werte zurückerlangen möchte.
Gegenmaßnahmen
Es gibt zwei Gegenmaßnahmen:
- Fragen Sie keine Objekte aus der Datenbank ab, die nicht in den Ergebnissen enthalten sein sollen. Verwenden Sie in den obigen Codeausschnitten beispielsweise nicht
Include
Foo.Bar
, wenn dieBar
-Instanz nicht aus der Datenbank zurückgegeben und in die Ergebnisse einbezogen werden soll. - Legen Sie den Wert der Navigation nach der Abfrage aus der Datenbank fest. Rufen Sie beispielsweise
foo.Bar = new()
in den obigen Codeausschnitten erst nach dem Ausführen der Abfrage auf.
Erwägen Sie außerdem, verknüpfte Entitätsinstanzen nicht als Standardobjekte zu initialisieren. Dadurch wird impliziert, dass es sich bei der verknüpften Instanz um eine neue Entität handelt, die nicht in der Datenbank gespeichert und für die kein Schlüsselwert festgelegt ist. Wenn die verknüpfte Entität jedoch in der Datenbank vorhanden ist, stehen die Daten im Code in einem grundsätzlichen Konflikt mit den in der Datenbank gespeicherten Daten.
Unbekannte Enumerationszeichenfolgenwerte in der Datenbank werden beim Abfragen nicht in den Enumerationsstandard konvertiert
Nachverfolgung von Issue 24084
Altes Verhalten
Enumerationseigenschaften können Zeichenfolgenspalten in der Datenbank mithilfe von HasConversion<string>()
oder EnumToStringConverter
zugeordnet werden. Dies führt dazu, dass EF Core Zeichenfolgenwerte in der Spalte in entsprechende Member des .NET-Enumerationstyps konvertiert. Wenn der Zeichenfolgenwert jedoch mit keinem Enumerationsmember übereinstimmte, wurde die Eigenschaft auf den Standardwert für die Enumeration festgelegt.
Neues Verhalten
EF Core 6.0 löst jetzt eine InvalidOperationException
mit der Meldung aus, dass der Zeichenfolgenwert {value}
aus der Datenbank nicht in einen Wert in der zugeordneten {enumType}
-Enumeration konvertiert werden kann.
Warum?
Die Konvertierung in den Standardwert kann zu einer Beschädigung der Datenbank führen, wenn die Entität später wieder in die Datenbank zurück gespeichert wird.
Gegenmaßnahmen
Stellen Sie im Idealfall sicher, dass die Datenbankspalte nur gültige Werte enthält. Implementieren Sie alternativ einen ValueConverter
mit dem alten Verhalten.
DbFunctionBuilder.HasTranslation stellt jetzt die Funktionsargumente als IReadOnlyList statt als IReadOnlyCollection bereit
Nachverfolgung von Issue 23565
Altes Verhalten
Beim Konfigurieren der Übersetzung für eine benutzerdefinierte Funktion mithilfe der HasTranslation
-Methode wurden die Argumente für die Funktion als IReadOnlyCollection<SqlExpression>
bereitgestellt.
Neues Verhalten
In EF Core 6.0 werden die Argumente jetzt als IReadOnlyList<SqlExpression>
bereitgestellt.
Warum?
IReadOnlyList
ermöglicht die Verwendung von Indexern, sodass jetzt einfacher auf die Argumente zugegriffen werden kann.
Gegenmaßnahmen
Keine. IReadOnlyList
implementiert die IReadOnlyCollection
-Schnittstelle, daher sollte der Übergang problemlos verlaufen.
Die Standardtabellenzuordnung wird nicht entfernt, wenn die Entität einer Tabellenwertfunktion zugeordnet wird
Nachverfolgung von Issue 23408
Altes Verhalten
Beim Zuordnen einer Entität zu einer Tabellenwertfunktion wurde die Standardzuordnung zu einer Tabelle entfernt.
Neues Verhalten
In EF Core 6.0 wird die Entität über die Standardzuordnung weiterhin einer Tabelle zugeordnet, selbst wenn sie einer Tabellenwertfunktion zugeordnet ist.
Warum?
Tabellenwertfunktionen, die Entitäten zurückgeben, werden häufig nicht als strikte Ersetzung der gesamten Tabelle verwendet, sondern entweder als Hilfsprogramm oder zum Kapseln eines Vorgangs, der eine Auflistung von Entitäten zurückgibt. Diese Änderung soll die wahrscheinliche Benutzerabsicht besser abbilden.
Gegenmaßnahmen
Die Zuordnung zu einer Tabelle kann in der Modellkonfiguration explizit deaktiviert werden:
modelBuilder.Entity<MyEntity>().ToTable((string)null);
dotnet-ef verwendet .NET 6 als Ziel
Nachverfolgung von Issue 27787
Altes Verhalten
Der Befehl „dotnet-ef“ verwendet schon seit einer Weile .NET Core 3.1 als Ziel. Dadurch konnten neuere Version des Tools verwendet werden, ohne neuere Versionen der .NET-Runtime zu installieren.
Neues Verhalten
In EF Core 6.0.6 verwendet das Tool „dotnet-ef“ nun .NET 6 als Ziel. Die Verwendung in Projekten, die ältere Versionen von .NET und .NET Core als Ziel verwenden, ist weiterhin möglich. Für die Ausführung des Tools muss jedoch die .NET 6-Runtime installiert werden.
Warum?
In Version 6.0.200 des .NET SDK wurde das Verhalten von dotnet tool install
in osx-arm64 aktualisiert, damit ein osx-x64-Shim für Tools erstellt wird, die .NET Core 3.1 als Ziel verwenden. Um die gewohnte Funktionsweise von dotnet-ef aufrechtzuerhalten, mussten wir das Ziel auf .NET 6 aktualisieren.
Gegenmaßnahmen
Sie können dotnet-ef ohne Installation der .NET 6-Runtime ausführen, indem Sie eine ältere Version des Tools installieren:
dotnet tool install dotnet-ef --version 3.1.*
IModelCacheKeyFactory
-Implementierungen müssen möglicherweise aktualisiert werden, um die Entwurfszeitzwischenspeicherung zu verarbeiten.
Nachverfolgung von Issue 25154
Altes Verhalten
IModelCacheKeyFactory
hatte keine Möglichkeit, das Entwurfszeitmodell separat vom Laufzeitmodell zwischenzuspeichern.
Neues Verhalten
IModelCacheKeyFactory
verfügt über eine neue Überladung, mit der das Entwurfszeitmodell separat vom Laufzeitmodell zwischengespeichert werden kann. Die Implementierung dieser Methode kann zu einer Ausnahme führen, die etwa wie folgt lautet:
System.InvalidOperationException: „Die angeforderte Konfiguration wird nicht im leseoptimierten Modell gespeichert. Verwenden Sie bitte ‚DbContext.GetService<IDesignTimeModel>().Model‘.“
Warum?
Die Implementierung kompilierter Modelle erforderte eine Trennung der Entwurfszeit (verwendet beim Erstellen des Modells) und der Laufzeitmodelle (die beim Ausführen von Abfragen usw.) verwendet werden. Wenn der Laufzeitcode Zugriff auf Entwurfszeitinformationen benötigt, muss das Entwurfszeitmodell zwischengespeichert werden.
Gegenmaßnahmen
Implementieren Sie die neue Überladung. Zum Beispiel:
public object Create(DbContext context, bool designTime)
=> context is DynamicContext dynamicContext
? (context.GetType(), dynamicContext.UseIntProperty, designTime)
: (object)context.GetType();
Die Navigation „{navigation}“ wurde ab „Include“ in der Abfrage ignoriert, da sie durch die Korrektur automatisch ausgefüllt wird. Wenn anschließend weitere Navigationen in „Include“ angegeben werden, werden sie ignoriert. Das Zurückgehen in der Include-Struktur ist nicht zulässig.
NavigationBaseIncludeIgnored
ist jetzt standardmäßig ein Fehler
Altes Verhalten
Das Ereignis CoreEventId.NavigationBaseIncludeIgnored
wurde standardmäßig als Warnung protokolliert.
Neues Verhalten
Das EreignisCoreEventId.NavigationBaseIncludeIgnored
wurde standardmäßig als Fehler protokolliert und führt zum Auslösen einer Ausnahme.
Warum?
Diese Abfragemuster sind nicht zulässig, daher weist EF Core nun darauf hin, dass die Abfragen aktualisiert werden müssen.
Gegenmaßnahmen
Das alte Verhalten kann wiederhergestellt werden, indem das Ereignis als Warnung konfiguriert wird. Zum Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.ConfigureWarnings(b => b.Warn(CoreEventId.NavigationBaseIncludeIgnored));