Breaking Changes in EF Core 5.0
Die folgenden API-Änderungen und Behavior Changes können dazu führen, dass vorhandene Anwendungen nach einem Upgrade auf EF Core 5.0.0 nicht mehr funktionieren.
Zusammenfassung
Änderungen mit mittlerer Auswirkung
EF Core 5.0 unterstützt .NET Framework nicht
Altes Verhalten
EF Core 3.1 ist auf .NET Standard 2.0 ausgelegt, was von .NET Framework unterstützt wird.
Neues Verhalten
EF Core 5.0 ist auf .NET Standard 2.1 ausgelegt, was nicht von .NET Framework unterstützt wird. Das bedeutet, EF Core 5.0 kann nicht mit .NET Framework-Anwendungen verwendet werden.
Warum?
Hierbei handelt es sich um einen Teil der umfassenderen Bewegung von .NET-Teams, um eine Vereinheitlichung in einem einzelnen .NET-Zielframework zu erreichen. Weitere Informationen finden Sie unter The future of .NET Standard (Die Zukunft von .NET Standard).
Gegenmaßnahmen
.NET Framework-Anwendungen können weiterhin EF Core 3.1 verwenden. Dabei handelt es sich um ein LTS-Release (Long-Term Support, langfristige Unterstützung). Alternativ können Anwendungen zur Verwendung von .NET Core 3.1 oder .NET 5 aktualisiert werden, um die Unterstützung von .NET Standard 2.1 zu nutzen.
IProperty.GetColumnName() gilt ab sofort als veraltet
Nachverfolgung von Issue #2266
Altes Verhalten
GetColumnName()
hat den Namen der Spalte zurückgegeben, der die Eigenschaft zugeordnet ist.
Neues Verhalten
GetColumnName()
gibt weiterhin den Namen der Spalte zurück, der eine Eigenschaft zugeordnet ist. Dieses Verhalten ist nun jedoch mehrdeutig, da EF Core 5 die TPT-Methode (Tabelle pro Typ) und gleichzeitiges Zuordnen zu einer Ansicht oder einer Funktion unterstützt, wenn diese Zuordnungen verschiedene Spaltennamen für dieselbe Eigenschaft verwenden könnten.
Warum?
Diese Methode wurde als veraltet gekennzeichnet, um Benutzern nahezulegen, eine exaktere Überladung zu verwenden: GetColumnName(IProperty, StoreObjectIdentifier).
Gegenmaßnahmen
Wenn der Entitätstyp immer nur einer einzigen Tabelle zugeordnet ist und niemals Ansichten, Funktionen oder mehreren Tabellen, kann die GetColumnBaseName(IReadOnlyProperty) in EF Core 5.0 und 6.0 verwendet werden, um den Tabellennamen zu erhalten. Beispiel:
var columnName = property.GetColumnBaseName();
In EF Core 7.0 kann dies wieder durch die neue GetColumnName
ersetzt werden, die sich wie das Original für einfache, nur eine Tabelle betreffende Zuordnungen verhält.
Wenn der Entitätstyp Ansichten, Funktionen oder mehreren Tabellen zugeordnet werden kann, muss ein StoreObjectIdentifier abgerufen werden, um die Tabelle, Ansicht oder Funktion zu identifizieren. Dies kann dann verwendet werden, um den Spaltennamen für dieses Speicherobjekt abzurufen. Beispiel:
var columnName = property.GetColumnName(StoreObjectIdentifier.Table("Users", null)));
Genauigkeit und Skalierung sind für Dezimale erforderlich
Nachverfolgung von Issue #19293
Altes Verhalten
EF Core hat die Genauigkeit und Skalierung von SqlParameter-Objekten normalerweise nicht festgelegt. Das bedeutet, dass die vollständige Genauigkeit und Skalierung an SQL Server übermittelt wurde, sodass SQL Server anhand der Genauigkeit und Skalierung der Datenbankspalte aufgerundet hat.
Neues Verhalten
Nun legt EF Core die Genauigkeit und Skalierung für Parameter mithilfe der Werte fest, die für Eigenschaften im EF Core-Modell konfiguriert wurden. Das bedeutet, dass die Rundung jetzt in SqlClient erfolgt. Demnach kann sich die Rundung ändern, wenn die Genauigkeit und Skalierung nicht mit der Genauigkeit und Skalierung der Datenbank übereinstimmen.
Warum?
Neuere SQL Server-Features, einschließlich Always Encrypted, erfordern, dass Parameterfacetten vollständig festgelegt werden. Darüber hinaus hat SqlClient eine Änderung an der Rundung vorgenommen, anstatt Dezimalwerte zu kürzen, was dem Verhalten von SQL Server entspricht. Dadurch konnte EF Core diese Facetten festlegen, ohne das Verhalten für ordnungsgemäß konfigurierte Dezimale zu ändern.
Gegenmaßnahmen
Ordnen Sie Ihre Dezimaleigenschaften mithilfe eines Typnamens zu, der die Genauigkeit und Skalierung enthält. Beispiel:
public class Blog
{
public int Id { get; set; }
[Column(TypeName = "decimal(16, 5)")]
public decimal Score { get; set; }
}
Alternativ können Sie HasPrecision
in den APIs für die Modellerstellung verwenden. Beispiel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().Property(e => e.Score).HasPrecision(16, 5);
}
Andere Semantik für die erforderliche oder Non-Nullable-Navigation vom Prinzipal zur abhängigen Entität
Altes Verhalten
Nur die Navigation zum Prinzipal konnte als erforderlich konfiguriert werden. Daher führte RequiredAttribute
, wenn es für die Navigation zur abhängigen Entität (die den Fremdschlüssel enthält) verwendet oder als Non-Nullable gekennzeichnet wurde, stattdessen zur Erstellung des Fremdschlüssels für den definierenden Entitätstyp.
Neues Verhalten
Dank der hinzugefügten Unterstützung von erforderlichen abhängigen Entitäten ist es nun möglich, jede beliebige Verweisnavigation als erforderlich zu markieren. Dies bedeutet, dass im obigen Fall der Fremdschlüssel auf der anderen Seite der Beziehung definiert wird und die Eigenschaften nicht als erforderlich markiert werden.
Das Aufrufen von IsRequired
vor der Angabe der abhängigen Entität ist nun mehrdeutig:
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
.IsRequired()
.HasForeignKey<BlogImage>(b => b.BlogForeignKey);
Warum?
Das neue Verhalten ist erforderlich, um die Unterstützung von erforderlichen abhängigen Entitäten zu ermöglichen (siehe 12100).
Gegenmaßnahmen
Entfernen Sie RequiredAttribute
aus der Navigation zur abhängigen Entität, und verwenden Sie „RequiredAttribute“ stattdessen in der Navigation zum Prinzipal, oder konfigurieren Sie die Beziehung in OnModelCreating
:
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
.HasForeignKey<BlogImage>(b => b.BlogForeignKey)
.IsRequired();
Die definierende Abfrage wurde durch anbieterspezifische Methoden ersetzt
Altes Verhalten
Entitätstypen wurden zum Definieren von Abfragen auf der Kernebene zugeordnet. Jedes Mal, wenn der Entitätstyp im Abfragestamm des Entitätstyps verwendet wurde, wurde er bei allen Anbietern durch die definierende Abfrage ersetzt.
Neues Verhalten
APIs für definierende Abfragen sind veraltet. Es wurden neue anbieterspezifische APIs eingeführt.
Warum?
Während definierende Abfragen als Ersatzabfragen implementiert wurden, wenn in der Abfrage der Abfragestamm verwendet wird, sind einige Probleme aufgetreten:
- Wenn eine definierende Abfrage einen Entitätstyp mit
new { ... }
in dieSelect
-Methode projiziert, war für die Identifizierung dessen als Entität zusätzliche Arbeit erforderlich. Zudem sorgte dieses Vorgehen für Inkonsistenzen in Bezug darauf, wie EF Core nominale Typen in der Abfrage behandelt. - Bei relationalen Anbietern ist
FromSql
weiterhin erforderlich, um die SQL-Zeichenfolge im LINQ-Ausdrucksformat zu übergeben.
Zunächst wurden definierende Abfragen als clientseitige Sichten zur Verwendung mit In-Memory-Anbietern bei schlüssellosen Entitäten eingeführt (ähnlich wie bei Datenbanksichten in relationalen Datenbanken). Eine solche Definition erleichtert das Testen der Anwendung mit In-Memory-Datenbanken. Später wurden sie allgemein anwendbar, was zwar nützlich war, aber ein inkonsistentes und schwer verständliches Verhalten mit sich brachte. Daher haben wir uns entschieden, das Konzept zu vereinfachen. Wir stellen die LINQ-basierte definierende Abfrage nur noch für In-Memory-Anbieter bereit und behandeln sie anders. Weitere Informationen findest du in diesem Issue.
Gegenmaßnahmen
Verwenden Sie für relationale Anbieter die ToSqlQuery
-Methode in OnModelCreating
, und übergeben Sie eine SQL-Zeichenfolge zur Verwendung für den Entitätstyp.
Verwenden Sie für den In-Memory-Anbieter die ToInMemoryQuery
-Methode in OnModelCreating
, und übergeben Sie eine LINQ-Abfrage zur Verwendung für den Entitätstyp.
Navigationen ohne Nullverweis werden von Abfragen nicht überschrieben
Altes Verhalten
In EF Core 3.1 wurden Verweisnavigationen, die frühzeitig mit Werten ungleich NULL initialisiert wurden, mitunter von Entitätsinstanzen aus der Datenbank überschrieben, und zwar unabhängig davon, ob Schlüsselwerte übereinstimmten oder nicht. In anderen Fällen hat EF Core 3.1 jedoch das Gegenteil bewirkt und den vorhandenen Wert ungleich NULL beibehalten.
Neues Verhalten
Ab EF Core 5.0 werden Verweisnavigationen ungleich NULL nicht mehr durch Instanzen überschrieben, die von einer Abfrage zurückgegeben werden.
Beachten Sie, dass die frühzeitige Initialisierung einer Sammlungsnavigation als leere Sammlung nach wie vor unterstützt wird.
Warum?
Die Initialisierung einer Verweisnavigationseigenschaft als „leere“ Entitätsinstanz führt zu einem uneindeutigen Zustand. Beispiel:
public class Blog
{
public int Id { get; set; }
public Author Author { get; set; ) = new Author();
}
Normalerweise erstellt eine Abfrage von Blogs und Autoren zunächst Blog
-Instanzen. Dann werden die entsprechenden Author
-Instanzen auf Grundlage der aus der Datenbank zurückgegebenen Daten festgelegt. In diesem Fall ist jedoch jede Blog.Author
-Eigenschaft bereits als leere Author
-Instanz initialisiert. Allerdings kann EF Core nicht wissen, dass diese Instanz „leer“ ist. Das Überschreiben dieser Instanz könnte also unbemerkt eine gültige Author
-Instanz auslösen. Daher überschreibt EF Core 5.0 jetzt konsequent keine Navigation, die bereits initialisiert ist.
Dieses neue Verhalten stimmt in den meisten Fällen auch mit dem Verhalten von EF6 überein, obwohl wir bei Untersuchungen auch einige Fälle von Inkonsistenz in EF6 gefunden haben.
Gegenmaßnahmen
Wenn dieses Problem auftritt, besteht die Lösung darin, die vorzeitige Initialisierung der Verweisnavigationseigenschaften zu beenden.
ToView() wird von Migrationen anders verarbeitet
Nachverfolgung von Issue #2725
Altes Verhalten
Das Aufrufen von ToView(string)
hat dazu geführt, dass Migrationen den Entitätstyp ignorieren und ihn einer Ansicht zuordnen.
Neues Verhalten
ToView(string)
markiert den Entitätstyp nun als nicht einer Tabelle zugeordnet und ordnet ihn einer Ansicht zu. Dies führt dazu, dass bei der ersten Migration nach einem Upgrade zu EF Core 5 versucht wird, die Standardtabelle für diesen Entitätstyp zu löschen, da er nicht mehr ignoriert wird.
Warum?
EF Core ermöglicht es nun, dass ein Entitätstyp gleichzeitig sowohl einer Tabelle als auch einer Ansicht zugeordnet werden kann. ToView
ist also kein gültiger Indikator dafür mehr, dass der Typ von Migrationen ignoriert werden soll.
Gegenmaßnahmen
Verwenden Sie den folgenden Code, um die zugeordnete Tabelle als von Migrationen ausgeschlossen zu markieren:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("UserView", t => t.ExcludeFromMigrations());
}
ToTable(null) markiert den Entitätstyp als nicht einer Tabelle zugeordnet
Nachverfolgung von Issue #21172
Altes Verhalten
ToTable(null)
hat den Tabellennamen auf den Standardwert zurückgesetzt.
Neues Verhalten
ToTable(null)
markiert den Entitätstyp nun als nicht einer Tabelle zugeordnet.
Warum?
EF Core ermöglicht es nun, dass ein Entitätstyp gleichzeitig sowohl einer Tabelle als auch einer Ansicht zugeordnet werden kann. ToTable(null)
wird also verwendet, um anzugeben, dass der Typ keiner Tabelle zugeordnet ist.
Gegenmaßnahmen
Verwenden Sie den folgenden Code, um den Tabellennamen in den Standardwert zurückzusetzen, wenn keine Zuordnung zu einer Ansicht oder einer DbFunction-Klasse vorliegt:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().Metadata.RemoveAnnotation(RelationalAnnotationNames.TableName);
}
Änderungen mit geringer Auswirkung
Die HasGeometricDimension-Methode wurde aus der SQLite NTS-Erweiterung entfernt.
Altes Verhalten
Die HasGeometricDimension-Methode wurde dazu verwendet, zusätzliche Maße (Z und M) für Geometriespalten zu ermöglichen. Dies hat sich jedoch immer nur auf die Datenbankerstellung ausgewirkt. Das Angeben dieser Methode zum Abfragen von Werten mit zusätzlichen Maßen war nicht notwendig. Außerdem hat die Methode nicht ordnungsgemäß funktioniert, wenn Werte mit zusätzlichen Maßen eingefügt oder geändert wurden (siehe Issue 14257).
Neues Verhalten
Zum Ermöglichen des Einfügens und Änderns von Geometriewerten mit zusätzlichen Maßen (Z und M) muss das jeweilige Maß als Teil des Spaltentypnamens angegeben werden. Diese API entspricht eher dem zugrunde liegenden Verhalten der AddGeometryColumn-Funktion von SpatiaLite.
Warum?
Die Verwendung der HasGeometricDimension-Methode, nachdem das Maß im Spaltentyp festgelegt wurde, ist redundant und nicht notwendig. Daher wurde die gesamte HasGeometricDimension-Methode entfernt.
Gegenmaßnahmen
Verwenden Sie HasColumnType
zum Festlegen der Maße:
modelBuilder.Entity<GeoEntity>(
x =>
{
// Allow any GEOMETRY value with optional Z and M values
x.Property(e => e.Geometry).HasColumnType("GEOMETRYZM");
// Allow only POINT values with an optional Z value
x.Property(e => e.Point).HasColumnType("POINTZ");
});
Azure Cosmos DB: Der Partitionsschlüssel wird nun dem Primärschlüssel hinzugefügt
Altes Verhalten
Die Partitionsschlüsseleigenschaft wurde nur dem alternativen Schlüssel hinzugefügt, der id
enthält.
Neues Verhalten
Die Partitionsschlüsseleigenschaft wird gemäß Konvention jetzt auch dem Primärschlüssel hinzugefügt.
Warum?
Durch diese Änderung ist das Modell besser auf die Azure Cosmos DB-Semantik abgestimmt. Zudem verbessert sich dadurch die Leistung von Find
und einigen Abfragen.
Gegenmaßnahmen
Um zu verhindern, dass die Partitionsschlüsseleigenschaft dem Primärschlüssel hinzugefügt wird, konfigurieren Sie sie in OnModelCreating
.
modelBuilder.Entity<Blog>()
.HasKey(b => b.Id);
Azure Cosmos DB: id
Eigenschaft umbenannt in __id
Altes Verhalten
Die der JSON-Eigenschaft id
zugeordnete Schatteneigenschaft hatte ebenfalls den Namen id
.
Neues Verhalten
Die gemäß Konvention erstellte Schatteneigenschaft trägt nun den Namen __id
.
Warum?
Durch diese Änderung ist es weniger wahrscheinlich, dass die Eigenschaft id
mit einer vorhandenen Eigenschaft des Entitätstyps kollidiert.
Gegenmaßnahmen
Wenn Sie zum Verhalten der Version 3.x zurückkehren möchten, konfigurieren Sie die Eigenschaft id
in OnModelCreating
.
modelBuilder.Entity<Blog>()
.Property<string>("id")
.ToJsonProperty("id");
Azure Cosmos DB: byte[] wird nun als Base64-Zeichenfolge statt als Zahlenarray gespeichert
Altes Verhalten
Eigenschaften vom Typ „byte[]“ wurden als Zahlenarray gespeichert.
Neues Verhalten
Eigenschaften vom Typ „byte[]“ werden nun als Base64-Zeichenfolge gespeichert.
Warum?
Diese Darstellung von „byte[]“ entspricht eher den Erwartungen. Zudem handelt es sich hier um das Standardverhalten der wichtigen JSON-Serialisierungsbibliotheken.
Gegenmaßnahmen
Als Zahlenarrays gespeicherte, bereits vorhandene Daten werden weiterhin ordnungsgemäß abgefragt. Derzeit wird jedoch keine Möglichkeit unterstützt, das Einfügeverhalten zu ändern. Wenn Ihr Szenario durch diese Einschränkung blockiert wird, kommentieren Sie dieses Problem.
Azure Cosmos DB: GetPropertyName und SetPropertyName wurden umbenannt
Altes Verhalten
Bisher wurden die Erweiterungsmethoden GetPropertyName
und SetPropertyName
genannt.
Neues Verhalten
Die alte API wurde entfernt, und es wurden die neuen Methoden GetJsonPropertyName
und SetJsonPropertyName
hinzugefügt.
Warum?
Durch diese Änderung wird die Mehrdeutigkeit um die Konfiguration durch diese Methoden beseitigt.
Gegenmaßnahmen
Verwenden Sie die neue API.
Wert-Generatoren werden aufgerufen, wenn der Entitätszustand von „Getrennt“ in „Unverändert“, „Aktualisiert“ oder „Gelöscht“ geändert wird
Altes Verhalten
Wert-Generatoren wurden nur aufgerufen, wenn der Entitätszustand in „Hinzugefügt“ geändert wurde.
Neues Verhalten
Wert-Generatoren werden jetzt aufgerufen, wenn der Entitätszustand von „Getrennt“ in „Unverändert“, „Aktualisiert“ oder „Gelöscht“ geändert wird und die Eigenschaft die Standardwerte enthält.
Warum?
Diese Änderung war erforderlich, um die Verwendung von Eigenschaften zu verbessern, die nicht dauerhaft im Datenspeicher gespeichert sind und deren Wert immer auf dem Client generiert wird.
Gegenmaßnahmen
Weisen Sie der Eigenschaft vor der Änderung des Status einen anderen Wert als den Standardwert zu, um zu verhindern, dass der Wert-Generator aufgerufen wird.
IMigrationsModelDiffer verwendet jetzt IRelationalModel
Altes Verhalten
Die IMigrationsModelDiffer
-API wurde mithilfe von IModel
definiert.
Neues Verhalten
Die IMigrationsModelDiffer
-API verwendet jetzt IRelationalModel
. Die Modellmomentaufnahme enthält jedoch weiterhin nur IModel
, da dieser Code Teil der Anwendung ist und Entity Framework ihn nicht ohne einen größeren Breaking Change ändern kann.
Warum?
IRelationalModel
ist eine neu hinzugefügte Darstellung des Datenbankschemas. Die Verwendung dieser Darstellung zum Ermitteln von Unterschieden ist schneller und präziser.
Gegenmaßnahmen
Verwenden Sie den folgenden Code, um das Modell von snapshot
mit dem Modell von context
zu vergleichen:
var dependencies = context.GetService<ProviderConventionSetBuilderDependencies>();
var relationalDependencies = context.GetService<RelationalConventionSetBuilderDependencies>();
var typeMappingConvention = new TypeMappingConvention(dependencies);
typeMappingConvention.ProcessModelFinalizing(((IConventionModel)modelSnapshot.Model).Builder, null);
var relationalModelConvention = new RelationalModelConvention(dependencies, relationalDependencies);
var sourceModel = relationalModelConvention.ProcessModelFinalized(snapshot.Model);
var modelDiffer = context.GetService<IMigrationsModelDiffer>();
var hasDifferences = modelDiffer.HasDifferences(
((IMutableModel)sourceModel).FinalizeModel().GetRelationalModel(),
context.Model.GetRelationalModel());
Eine Verbesserung dieser Funktion in Version 6.0 ist geplant (siehe 22031).
Diskriminatoren sind schreibgeschützt
Altes Verhalten
Der Diskriminatorwert konnte vor dem Aufrufen von SaveChanges
geändert werden.
Neues Verhalten
Im obigen Fall wird eine Ausnahme ausgelöst.
Warum?
EF erwartet nicht, dass der Entitätstyp geändert wird, während er noch nachverfolgt wird, sodass das Ändern des Diskriminatorwerts zu einem inkonsistenten Kontext führt. Dies hat möglicherweise ein unerwartetes Verhalten zur Folge.
Gegenmaßnahmen
Wenn eine Änderung des Diskriminatorwerts erforderlich ist und der Kontext direkt nach dem Aufruf von SaveChanges
verworfen wird, kann der Diskriminator als änderbar festgelegt werden:
modelBuilder.Entity<BaseEntity>()
.Property<string>("Discriminator")
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save);
Anbieterspezifische EF.Functions-Methoden werden für InMemory-Anbieter ausgelöst.
Altes Verhalten
Anbieterspezifische EF.Functions-Methoden enthielten eine Implementierung für die Clientausführung, sodass sie für den InMemory-Anbieter ausgeführt werden konnten. EF.Functions.DateDiffDay
ist beispielsweise eine SQL Server-spezifische Methode, die für InMemory-Anbieter galt.
Neues Verhalten
Anbieterspezifische Methoden wurden aktualisiert, sodass sie im Methodentext eine Ausnahme auslösen, um ihre Auswertung auf Clientseite zu blockieren.
Warum?
Anbieterspezifische Methoden sind einer Datenbankfunktion zugeordnet. Das Berechnungsergebnis der zugeordneten Datenbankfunktion kann auf Clientseite in LINQ nicht immer repliziert werden. Dies kann dazu führen, dass sich das Ergebnis von dem des Servers unterscheidet, wenn dieselbe Methode auf dem Client ausgeführt wird. Da diese Methoden in LINQ verwendet werden, um Übersetzungen in bestimmte Datenbankfunktionen durchzuführen, müssen sie nicht auf Clientseite ausgewertet werden. Da der InMemory-Anbieter eine andere Datenbank ist, sind die Methoden für diesen Anbieter nicht verfügbar. Wenn Sie versuchen, sie für den InMemory-Anbieter oder einen anderen Anbieter auszuführen, der diese Methoden nicht übersetzt, wird eine Ausnahme ausgelöst.
Gegenmaßnahmen
Da es keine Möglichkeit gibt, das Verhalten von Datenbankfunktionen exakt zu imitieren, sollten Sie die Abfragen, die diese Methoden enthalten, mit demselben Datenbanktyp testen, der auch in der Produktionsumgebung verwendet wird.
IndexBuilder.HasName ist mittlerweile veraltet
Nachverfolgung von Issue 21089
Altes Verhalten
Bisher konnte nur ein Index für eine bestimmte Gruppe von Eigenschaften definiert werden. Der Datenbankname eines Indexes wurde mithilfe von IndexBuilder.HasName konfiguriert.
Neues Verhalten
Für dieselbe Gruppe oder dieselben Eigenschaften sind jetzt mehrere Indizes zulässig. Diese Indizes unterscheiden sich nun durch einen Namen im Modell. Gemäß der Konvention wird der Modellname als Datenbankname verwendet. Er kann jedoch auch unabhängig mit HasDatabaseName konfiguriert werden.
Warum?
In Zukunft möchten wir sowohl aufsteigende als auch absteigende Indizes oder Indizes mit unterschiedlichen Sortierungen für denselben Satz von Eigenschaften aktivieren. Diese Änderung ist ein weiterer Schritt in diese Richtung.
Gegenmaßnahmen
Sämtlicher Code, der zuvor IndexBuilder.HasName aufgerufen hat, sollte so aktualisiert werden, dass stattdessen HasDatabaseName aufgerufen wird.
Wenn das Projekt Migrationen enthält, die vor EF Core Version 2.0.0 generiert wurden, können Sie die Warnung in diesen Dateien problemlos ignorieren und unterdrücken, indem Sie #pragma warning disable 612, 618
hinzufügen.
Ein Pluralizer ist nun für den Gerüstbau für Modelle mit Reverse Engineering enthalten
Nachverfolgung von Issue 11160
Altes Verhalten
Bislang mussten Sie beim Gerüstbau für einen Datenbankkontext und Entitätstypen durch Reverse Engineering eines Datenbankschemas ein separates Pluralizer-Paket installieren, um DbSet- und Sammlungsnavigationsnamen in die Pluralform und Tabellennamen in die Singularform umzuwandeln.
Neues Verhalten
EF Core enthält jetzt einen Pluarlizer, der die Humanizer-Bibliothek verwendet. Dies ist dieselbe Bibliothek, die Visual Studio verwendet, um Variablennamen zu empfehlen.
Warum?
Die Verwendung von Pluralformen von Wörtern für Sammlungseigenschaften und Singularformen für Typen und Verweiseigenschaften ist in .NET idiomatisch.
Gegenmaßnahmen
Um den Pluralizer zu deaktivieren, verwenden Sie die Option --no-pluralize
auf dotnet ef dbcontext scaffold
oder den -NoPluralize
-Switch auf Scaffold-DbContext
.
INavigationBase ersetzt INavigation in einigen APIs zur Unterstützung überspringender Navigationen
Altes Verhalten
Bis Version 5.0 unterstützte EF Core nur eine Form der Navigationseigenschaft, die durch die INavigation
-Schnittstelle dargestellt wird.
Neues Verhalten
EF Core 5.0 führt m:n-Beziehungen ein, die „überspringende Navigationen“ verwenden. Diese werden durch die ISkipNavigation
-Schnittstelle dargestellt, und der größte Teil der Funktionalität von INavigation
wurde auf die gemeinsame Basisschnittstelle INavigationBase
verlagert.
Warum?
Die meisten Funktionen sind bei normalen und überspringenden Navigationen identisch. Allerdings haben überspringende Navigationen eine andere Beziehung zu Fremdschlüsseln als normale Navigationen, da sich die beteiligten Fremdschlüssel nicht direkt an beiden Enden der Beziehung befinden, sondern in der JOIN-Entität.
Gegenmaßnahmen
In vielen Fällen können Anwendungen ohne weitere Änderungen zur Verwendung der neuen Basisschnittstelle übergehen. In Fällen jedoch, in denen die Navigation für den Zugriff auf Fremdschlüsseleigenschaften verwendet wird, sollte der Code der Anwendung entweder nur auf normale Navigationen beschränkt oder so aktualisiert werden, dass er sowohl bei normalen als auch bei überspringenden Navigationen das Richtige tut.
Einige Abfragen mit korrelierter Sammlung, die ebenfalls Distinct
oderGroupBy
verwenden, werden nicht mehr unterstützt
Nachverfolgung von Issue 15873
Altes Verhalten
Zuvor haben wir die Ausführung von Abfragen mit korrelierten Sammlungen, gefolgt von GroupBy
, sowie einige Abfragen mit Distinct
erlaubt.
Beispiel für GroupBy:
context.Parents
.Select(p => p.Children
.GroupBy(c => c.School)
.Select(g => g.Key))
Beispiel für Distinct
: insbesondere Distinct
-Abfragen, bei denen die innere Sammlungsprojektion nicht den Primärschlüssel enthält:
context.Parents
.Select(p => p.Children
.Select(c => c.School)
.Distinct())
Diese Abfragen konnten falsche Ergebnisse liefern, wenn die innere Sammlung Duplikate enthielt, funktionierten aber einwandfrei, wenn alle Elemente in der inneren Sammlung eindeutig waren.
Neues Verhalten
Diese Abfragen werden nicht mehr unterstützt. Es wird eine Ausnahme ausgelöst, die darauf hinweist, dass wir nicht genügend Informationen haben, um die Ergebnisse ordnungsgemäß zu erstellen.
Hintergründe
Für Szenarien mit korrelierten Sammlungen müssen wir den Primärschlüssel der Entitäten kennen, um die Sammlungsentitäten dem richtigen übergeordneten Element zuweisen zu können. Wenn die innere Sammlung GroupBy
oder Distinct
nicht verwendet, kann der fehlende Primärschlüssel einfach zur Projektion hinzugefügt werden. Im Falle von GroupBy
und Distinct
ist dies jedoch nicht möglich, weil es das Ergebnis des Vorgangs GroupBy
oder Distinct
ändern würde.
Gegenmaßnahmen
Schreiben Sie die Abfrage so um, dass die Vorgänge GroupBy
oder Distinct
nicht auf die innere Sammlung angewendet werden. Führen Sie diese Vorgänge stattdessen auf dem Client aus.
context.Parents
.Select(p => p.Children.Select(c => c.School))
.ToList()
.Select(x => x.GroupBy(c => c).Select(g => g.Key))
context.Parents
.Select(p => p.Children.Select(c => c.School))
.ToList()
.Select(x => x.Distinct())
Die Verwendung einer Sammlung abfragbarer Typen in der Projektion wird nicht unterstützt
Nachverfolgung von Issue 16314
Altes Verhalten
Bislang war es in einigen Fällen möglich, eine Sammlung eines abfragbaren Typs innerhalb der Projektion zu verwenden, z. B. als Argument für einen List<T>
-Konstruktor:
context.Blogs
.Select(b => new List<Post>(context.Posts.Where(p => p.BlogId == b.Id)))
Neues Verhalten
Diese Abfragen werden nicht mehr unterstützt. Eine Ausnahme wird ausgelöst, die darauf hinweist, dass wir kein Objekt des abfragbaren Typs erstellen können, und vorschlägt, wie dies korrigiert werden könnte.
Hintergründe
Wir können ein Objekt eines abfragbaren Typs nicht materialisieren. Daher würde es stattdessen automatisch mit dem Typ List<T>
erstellt werden. Dies führte oft zu einer Ausnahme aufgrund nicht übereinstimmender Typen, was nicht sehr klar wurde und für einige Benutzer überraschend sein konnte. Wir haben uns entschieden, das Muster zu erkennen und eine aussagekräftigere Ausnahme auszulösen.
Gegenmaßnahmen
Fügen Sie hinter dem abfragbaren Objekt in der Projektion einen Aufruf von ToList()
hinzu:
context.Blogs.Select(b => context.Posts.Where(p => p.BlogId == b.Id).ToList())