Breaking Changes in EF Core 9 (EF9)
Auf dieser Seite werden API-Änderungen und Behavior Changes dokumentiert, die bei einem Update von EF Core 8 auf EF Core 9 zu Problemen mit bestehenden Anwendungen führen können. Überprüfen Sie frühere grundlegende Änderungen, wenn sie von einer früheren Version von EF Core aktualisiert werden:
Zielframework
EF Core 9 zielt auf .NET 8 ab. Dies bedeutet, dass vorhandene Anwendungen, die auf .NET 8 abzielen, dies weiterhin tun können. Anwendungen für ältere .NET-, .NET Core- und .NET Framework-Versionen müssen .NET 8 oder .NET 9 für die Verwendung von EF Core 9 verwenden.
Zusammenfassung
Hinweis
Wenn Sie Azure Cosmos DB verwenden, lesen Sie den separaten Abschnitt unten zu Azure Cosmos DB-Änderungen.
Wichtige Änderung | Auswirkung |
---|---|
EF.Functions.Unhex() gibt jetzt byte[]? zurück |
Niedrig |
Die NULL-Zulässigkeitsargumente von SqlFunctionExpression | Niedrig |
ToString() -Methode gibt jetzt für null -Instanzen eine leere Zeichenfolge zurück |
Niedrig |
Abhängigkeiten des freigegebenen Frameworks wurden auf 9.0.x aktualisiert | Niedrig |
Änderungen mit geringer Auswirkung
EF.Functions.Unhex()
gibt jetzt byte[]?
zurück
Nachverfolgung von Issue 33864
Altes Verhalten
Die EF.Functions.Unhex()
Funktion wurde zuvor mit Anmerkungen versehen, um byte[]
zurückzugeben.
Neues Verhalten
Ab EF Core 9.0 wird Unhex() nun kommentiert, um byte[]?
zurückzugeben.
Warum?
Unhex()
wird in die SQLite-Funktion unhex
übersetzt, die bei ungültigen Eingaben NULL zurückgibt. Infolgedessen gab Unhex()
für diese Fälle null
zurück, was einen Verstoß gegen die Anmerkung darstellt.
Gegenmaßnahmen
Wenn Sie sicher sind, dass der an Unhex()
übergebene Textinhalt eine gültige hexadezimale Zeichenkette darstellt, können Sie einfach den Null-Forgiving-Operator als Zusicherung hinzufügen, dass der Aufruf niemals Null zurückgibt:
var binaryData = await context.Blogs.Select(b => EF.Functions.Unhex(b.HexString)!).ToListAsync();
Fügen Sie andernfalls Laufzeitüberprüfungen auf NULL für den Rückgabewert von Unhex() hinzu.
NULL-Zulässigkeitsargumente von SqlFunctionExpression
Nachverfolgung von Issue 33852
Altes Verhalten
Zuvor war es möglich, eine SqlFunctionExpression
mit einer anderen Anzahl von Argumenten und NULL-Zulässigkeitsverteilungsargumenten zu erstellen.
Neues Verhalten
Ab EF Core 9.0 löst EF jetzt aus, wenn die Anzahl von Argumenten und NULL-Zulässigkeitsverteilungsargumenten nicht übereinstimmen.
Warum?
Es kann zu unerwartetem Verhalten führen, dass keine übereinstimmende Anzahl von Argumenten und Verteilungsargumenten zur NULL-Zulässigkeit vorhanden sind.
Gegenmaßnahmen
Stellen Sie sicher, dass die argumentsPropagateNullability
dieselbe Anzahl von Elementen wie die arguments
aufweist. Verwenden Sie im Zweifelsfall false
für das NULL-Zulässigkeitsargument.
ToString()
-Methode gibt jetzt für null
-Instanzen eine leere Zeichenfolge zurück
Nachverfolgung von Issue 33941
Altes Verhalten
Zuvor wurden von EF inkonsistente Ergebnisse für die ToString()
-Methode zurückgegeben, wenn der Argumentwert null
war. So wurde beispielsweise bei ToString()
für die Eigenschaft bool?
mit dem Wert null
der Wert null
zurückgegeben, aber bei Ausdrücken ohne die Eigenschaft bool?
, deren Wert null
war, wurde True
zurückgegeben. Das Verhalten war auch für andere Datentypen inkonsistent. So wurde beispielsweise bei ToString()
für die null
-Wertenumeration eine leere Zeichenfolge zurückgegeben.
Neues Verhalten
Ab EF Core 9.0 gibt die Methode ToString()
nun konsistent in allen Fällen eine leere Zeichenfolge zurück, wenn der Argumentwert null
ist.
Warum?
Das alte Verhalten war bei unterschiedlichen Datentypen und in unterschiedlichen Situationen inkonsistent und nicht auf das Verhalten von C# abgestimmt.
Gegenmaßnahmen
Wenn Sie zum alten Verhalten zurückkehren möchten, schreiben Sie die Abfrage entsprechend um:
var newBehavior = context.Entity.Select(x => x.NullableBool.ToString());
var oldBehavior = context.Entity.Select(x => x.NullableBool == null ? null : x.NullableBool.ToString());
Abhängigkeiten des freigegebenen Frameworks wurden auf 9.0.x aktualisiert
Altes Verhalten
Apps, die das Microsoft.NET.Sdk.Web
SDK verwenden und für net8.0 konzipiert sind, lösen Pakete wie System.Text.Json
, Microsoft.Extensions.Caching.Memory
, Microsoft.Extensions.Configuration.Abstractions
, Microsoft.Extensions.Logging
und Microsoft.Extensions.DependencyModel
über das freigegebene Framework auf. Daher werden diese Assemblys normalerweise nicht zusammen mit der App bereitgestellt.
Neues Verhalten
EF Core 9.0 unterstützt zwar weiterhin net8.0, verweist nun aber auf die 9.0.x-Versionen von System.Text.Json
, Microsoft.Extensions.Caching.Memory
, Microsoft.Extensions.Configuration.Abstractions
, Microsoft.Extensions.Logging
und Microsoft.Extensions.DependencyModel
. Apps, die für net8.0 konzipiert sind, können das freigegebene Framework nicht nutzen, um die Bereitstellung dieser Assemblys zu vermeiden.
Warum?
Die entsprechenden Abhängigkeitsversionen enthalten die neuesten Sicherheitskorrekturen, und ihre Verwendung vereinfacht das Wartungsmodell für EF Core.
Gegenmaßnahmen
Richten Sie Ihre App auf net9.0 aus, um das vorherige Verhalten zu erhalten.
Breaking Changes für Azure Cosmos DB
Der Azure Cosmos DB-Anbieter wurde in der Version 9.0 durch zahlreiche Maßnahmen verbessert. Die Änderungen umfassen eine Reihe von Änderungen mit hoher Auswirkung; Wenn Sie ein Upgrade einer vorhandenen Anwendung durchführen, lesen Sie bitte Folgendes sorgfältig.
Änderungen mit hoher Auswirkung
Die Diskriminatoreigenschaft wird jetzt $type
anstelle von Discriminator
genannt
Altes Verhalten
EF fügt JSON-Dokumenten automatisch eine Diskriminatoreigenschaft hinzu, um den Entitätstyp zu identifizieren, den das Dokument darstellt. In früheren Versionen von EF wurde diese JSON-Eigenschaft standardmäßig Discriminator
genannt.
Neues Verhalten
Ab EF Core 9.0 wird die Diskriminatoreigenschaft jetzt standardmäßig $type
genannt. Falls bei Ihnen in Azure Cosmos DB Dokumente aus früheren Versionen von EF vorhanden sind, verwenden diese die alte Benennung Discriminator
, und nach dem Upgrade auf EF 9.0 tritt bei Abfragen für diese Dokumente ein Fehler auf.
Warum?
Eine neue JSON-Praxis verwendet eine $type
Eigenschaft in Szenarien, in denen der Typ eines Dokuments identifiziert werden muss. System.Text.Json von .NET unterstützt zum Beispiel auch Polymorphismus, wobei $type
der Standardname der Diskriminatoreigenschaft (Docs) verwendet. Um den Rest des Ökosystems auszurichten und die Zusammenarbeit mit externen Tools zu vereinfachen, wurde die Standardeinstellung geändert.
Gegenmaßnahmen
Die einfachste Entschärfung besteht darin, einfach den Namen der Diskriminatoreigenschaft so Discriminator
zu konfigurieren, wie zuvor:
modelBuilder.Entity<Session>().HasDiscriminator<string>("Discriminator");
Dadurch verhält sich EF für alle Entitätstypen auf oberster Ebene genau wie zuvor.
Wenn Sie möchten, können Sie auch alle Ihre Dokumente so aktualisieren, dass sie die neue $type
Benennung verwenden.
Die id
Eigenschaft enthält jetzt standardmäßig nur die EF-Schlüsseleigenschaft.
Altes Verhalten
Zuvor hat EF den Diskriminatorwert Ihres Entitätstyps in die id
Eigenschaft des Dokuments eingefügt. Wenn Sie beispielsweise einen Entitätstyp Blog
mit einer Id
Eigenschaft mit 8 gespeichert haben, würde die JSON-Eigenschaft id
enthalten Blog|8
.
Neues Verhalten
Ab EF Core 9.0 enthält die JSON-Eigenschaft id
nicht mehr den Diskriminatorwert, sondern nur noch den Wert Ihrer Schlüsseleigenschaft. Im obigen Beispiel wäre die JSON-Eigenschaft id
einfach 8
sein. Falls bei Ihnen in Azure Cosmos DB Dokumente aus früheren Versionen von EF vorhanden sind, weisen diese den Diskriminatorwert in der JSON-Eigenschaft id
auf, und nach dem Upgrade auf EF 9.0 tritt bei Abfragen für diese Dokumente ein Fehler auf.
Warum?
Da die JSON-Eigenschaft id
eindeutig sein muss, wurde früher der Diskriminator hinzugefügt, um die Existenz unterschiedlicher Entitäten mit dem gleichen Schlüsselwert zu ermöglichen. Dadurch konnten beispielsweise innerhalb des gleichen Containers und der gleichen Partition ein Blog
und ein Post
mit einer Id
-Eigenschaft vorhanden sein, die den Wert 8 enthält. Dies passte besser zu Datenmodellierungsmustern relationaler Datenbanken, bei denen jeder Entitätstyp einer eigenen Tabelle zugeordnet ist und somit über einen eigenen Schlüsselraum verfügt.
EF 9.0 hat allgemein die Zuordnung verändert, um sie besser an gängige Azure Cosmos DB-NoSQL-Praktiken und -Erwartungen anzupassen, anstatt den Erwartungen von Benutzern zu entsprechen, die von relationalen Datenbanken kommen. Darüber hinaus erschwerte das Verwenden des Diskriminatorwerts in der id
Eigenschaft die Interaktion mit externen Tools und Systemen mit EF-generierten JSON-Dokumenten. Solche externen Systeme sind den EF-Diskriminatorwerten, die standardmäßig von .NET-Typen abgeleitet sind, nicht bekannt.
Gegenmaßnahmen
Die einfachste Entschärfung besteht darin, EF so zu konfigurieren, dass er den Diskriminator wie zuvor in die JSON-Eigenschaft id
einschließt. Zu diesem Zweck wurde eine neue Konfigurationsoption eingeführt:
modelBuilder.Entity<Session>().HasDiscriminatorInJsonId();
Dadurch verhält sich EF für alle Entitätstypen auf oberster Ebene genau wie zuvor.
Wenn Sie möchten, können Sie auch alle Ihre Dokumente aktualisieren, um ihre JSON-Eigenschaft id
neu zu schreiben. Beachten Sie, dass dies nur möglich ist, wenn Entitäten unterschiedlicher Typen nicht den gleichen ID-Wert innerhalb desselben Containers gemeinsam verwenden.
Änderungen mit mittlerer Auswirkung
Die Synchronisierung von E/A über den Azure Cosmos DB-Anbieter wird nicht mehr unterstützt
Nachverfolgung von Issue 32563
Altes Verhalten
Das Aufrufen synchroner Methoden wie ToList
oder SaveChanges
würde dazu führen, dass EF Core beim Ausführen asynchroner Aufrufe für das Azure Cosmos DB SDK synchron mit .GetAwaiter().GetResult()
blockiert wird. Dies kann zu Deadlock führen.
Neues Verhalten
Ab EF Core 9.0 wird EF jetzt standardmäßig ausgelöst, wenn versucht wird, synchronen E/A zu verwenden. Die Ausnahmemeldung lautet „Azure Cosmos DB unterstützt keinen synchronen E/A.“ Stellen Sie sicher, dass Sie nur asynchrone Methoden verwenden und richtig auf sie warten, wenn Sie Entity Framework Core für den Zugriff auf Azure Cosmos DB verwenden. Weitere Informationen finden Sie unter https://aka.ms/ef-cosmos-nosync.
Warum?
Synchrone Blockierung von asynchronen Methoden kann zu Deadlock führen, und das Azure Cosmos DB SDK unterstützt nur asynchrone Methoden.
Gegenmaßnahmen
In EF Core 9.0 kann der Fehler unterdrückt werden mit:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ConfigureWarnings(w => w.Ignore(CosmosEventId.SyncNotSupported));
}
Das heißt, Anwendungen sollten die Verwendung von Synchronisierungs-APIs mit Azure Cosmos DB beenden, da dies nicht vom Azure Cosmos DB SDK unterstützt wird. Die Möglichkeit, die Ausnahme zu unterdrücken, wird in einer zukünftigen Version von EF Core entfernt, wonach die einzige Option die Verwendung asynchroner APIs sein wird.
SQL-Abfragen müssen jetzt JSON-Werte direkt projizieren
Altes Verhalten
Zuvor generierte EF Abfragen wie die folgenden:
SELECT c["City"] FROM root c
Solche Abfragen führen dazu, dass Azure Cosmos DB jedes Ergebnis wie folgt in ein JSON-Objekt einschließt:
[
{
"City": "Berlin"
},
{
"City": "México D.F."
}
]
Neues Verhalten
Ab EF Core 9.0 fügt EF nun den VALUE
Modifizierer zu Abfragen wie folgt hinzu:
SELECT VALUE c["City"] FROM root c
Solche Abfragen führen dazu, dass Azure Cosmos DB die Werte direkt zurückgibt, ohne sie einzuschließen:
[
"Berlin",
"México D.F."
]
Wenn Ihre Anwendung SQL-Abfragen verwendet, werden solche Abfragen wahrscheinlich nach dem Upgrade auf EF 9.0 unterbrochen, da sie den VALUE
Modifizierer nicht enthalten.
Warum?
Jedes Ergebnis in ein zusätzliches JSON-Objekt zu verpacken, kann in einigen Szenarien zu Leistungseinbußen führen, vergrößert die JSON-Ergebnis-Nutzlast und ist nicht die optimale Art, mit Azure Cosmos DB zu arbeiten.
Gegenmaßnahmen
Um dies zu vermeiden, fügen Sie einfach den VALUE
Modifizierer zu den Projektionen Ihrer SQL-Abfragen hinzu, wie oben gezeigt.
Nicht definierte Ergebnisse werden jetzt automatisch aus Abfrageergebnissen gefiltert
Altes Verhalten
Zuvor generierte EF Abfragen wie die folgenden:
SELECT c["City"] FROM root c
Solche Abfragen führen dazu, dass Azure Cosmos DB jedes Ergebnis wie folgt in ein JSON-Objekt einschließt:
[
{
"City": "Berlin"
},
{
"City": "México D.F."
}
]
Wenn eines der Ergebnisse nicht definiert war (z. B. die City
Eigenschaft fehlte im Dokument), wurde ein leeres Dokument zurückgegeben, und EF würde null
für dieses Ergebnis zurückgeben.
Neues Verhalten
Ab EF Core 9.0 fügt EF nun den VALUE
Modifizierer zu Abfragen wie folgt hinzu:
SELECT VALUE c["City"] FROM root c
Solche Abfragen führen dazu, dass Azure Cosmos DB die Werte direkt zurückgibt, ohne sie einzuschließen:
[
"Berlin",
"México D.F."
]
Das Azure Cosmos DB-Verhalten besteht darin, undefined
-Werte automatisch aus Ergebnissen zu filtern. Wenn also eine der City
-Eigenschaften nicht im Dokument vorhanden ist, gibt die Abfrage nur ein einzelnes Ergebnis zurück, und das andere ist null
.
Warum?
Jedes Ergebnis in ein zusätzliches JSON-Objekt zu verpacken, kann in einigen Szenarien zu Leistungseinbußen führen, vergrößert die JSON-Ergebnis-Nutzlast und ist nicht die optimale Art, mit Azure Cosmos DB zu arbeiten.
Gegenmaßnahmen
Wenn das Abrufen von null
Werten für nicht definierte Ergebnisse für Ihre Anwendung wichtig ist, werden die undefined
Werte zu null
zusammengefasst, um den neuen EF.Functions.Coalesce
Operator zu verwenden:
var users = await context.Customer
.Select(c => EF.Functions.CoalesceUndefined(c.City, null))
.ToListAsync();
Falsch übersetzte Abfragen werden nicht mehr übersetzt
Altes Verhalten
Zuvor übersetzte EF Abfragen wie die folgende:
var sessions = await context.Sessions
.Take(5)
.Where(s => s.Name.StartsWith("f"))
.ToListAsync();
Die SQL-Übersetzung für diese Abfrage war jedoch falsch:
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Session") AND STARTSWITH(c["Name"], "f"))
OFFSET 0 LIMIT @__p_0
In SQL wird die WHERE
Klausel vor den OFFSET
und LIMIT
Klauseln ausgewertet. In der obigen LINQ-Abfrage wird der Take
Operator jedoch vor dem Where
Operator angezeigt. Daher könnten solche Abfragen falsche Ergebnisse zurückgeben.
Neues Verhalten
Ab EF Core 9.0 werden solche Abfragen nicht mehr übersetzt, und eine Ausnahme wird ausgelöst.
Warum?
Falsche Übersetzungen können zu einer automatischen Datenbeschädigung führen, was zu schwer zu erkennenden Fehlern in Ihrer Anwendung führen kann. EF bevorzugen es immer, schnell zu ausfallen, indem sie vorab ausgelöst werden, anstatt möglicherweise zu Datenbeschädigungen zu führen.
Gegenmaßnahmen
Wenn Sie mit dem vorherigen Verhalten zufrieden waren und dasselbe SQL ausführen möchten, tauschen Sie einfach die Reihenfolge der LINQ-Operatoren aus:
var sessions = await context.Sessions
.Where(s => s.Name.StartsWith("f"))
.Take(5)
.ToListAsync();
Leider unterstützt Azure Cosmos DB die Klauseln OFFSET
und LIMIT
in SQL-Unterabfragen derzeit nicht, was für die ordnungsgemäße Übersetzung der ursprünglichen LINQ-Abfrage erforderlich ist.
Änderungen mit geringer Auswirkung
HasIndex
wird jetzt ausgelöst, anstatt ignoriert zu werden
Altes Verhalten
Zuvor wurden Aufrufe HasIndex vom EF Cosmos DB-Anbieter ignoriert.
Neues Verhalten
Der Anbieter löst jetzt aus, wenn HasIndex angegeben wird.
Warum?
In Azure Cosmos DB werden alle Eigenschaften standardmäßig indiziert, und es muss keine Indizierung angegeben werden. Obwohl es möglich ist, eine benutzerdefinierte Indizierungsrichtlinie zu definieren, wird dies derzeit nicht von EF unterstützt und kann über das Azure-Portal ohne EF-Unterstützung erfolgen. Da HasIndex Aufrufe nichts getan haben, sind sie nicht mehr zulässig.
Gegenmaßnahmen
Entfernen Sie alle Aufrufe von HasIndex.
IncludeRootDiscriminatorInJsonId
wurde nach 9.0.0-rc.2 in HasRootDiscriminatorInJsonId
umbenannt
Altes Verhalten
Die API IncludeRootDiscriminatorInJsonId
wurde in 9.0.0 rc.1 eingeführt.
Neues Verhalten
Für die endgültige Version von EF Core 9.0 wurde die API umbenannt in HasRootDiscriminatorInJsonId
Warum?
Eine andere verwandte API wurde umbenannt, um mit Has
statt Include
zu beginnen, und daher wurde diese API auch für Konsistenz umbenannt.
Gegenmaßnahmen
Wenn Ihr Code die IncludeRootDiscriminatorInJsonId
API verwendet, ändern Sie ihn einfach so, dass er stattdessen HasRootDiscriminatorInJsonId
referenziert.