Breaking Changes in EF Core 8 (EF8)
Auf dieser Seite werden API-Änderungen und Behavior Changes dokumentiert, die bei einem Update von EF Core 7 auf EF Core 8 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 8 zielt auf .NET 8 ab. Anwendungen für ältere .NET-, .NET Core- und .NET Framework-Versionen müssen auf .NET 8 aktualisiert werden.
Zusammenfassung
Änderungen mit hoher Auswirkung
Contains
in LINQ-Abfragen funktioniert möglicherweise nicht mehr auf älteren SQL Server-Versionen
Nachverfolgung von Issue #13617
Altes Verhalten
EF bot spezielle Unterstützung für LINQ-Abfragen mit dem Contains
-Operator für eine Liste parametrisierter Werte:
var names = new[] { "Blog1", "Blog2" };
var blogs = await context.Blogs
.Where(b => names.Contains(b.Name))
.ToArrayAsync();
Vor EF Core 8.0 hat EF die parametrisierten Werte als Konstanten in die SQL eingefügt:
SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] IN (N'Blog1', N'Blog2')
Neues Verhalten
Ab EF Core 8.0 generiert EF jetzt SQL, das in vielen Fällen effizienter ist, wird aber in SQL Server 2014 und unten nicht unterstützt:
SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] IN (
SELECT [n].[value]
FROM OPENJSON(@__names_0) WITH ([value] nvarchar(max) '$') AS [n]
)
Beachten Sie, dass neuere SQL Server-Versionen möglicherweise mit einer älteren Kompatibilitätsstufe konfiguriert werden, wodurch sie ebenfalls nicht mit dem neuen SQL kompatibel sind. Dies kann auch mit einer Azure SQL-Datenbank auftreten, die von einer früheren lokalen SQL Server-Instanz migriert wurde, wobei die alte Kompatibilitätsstufe übernommen wird.
Warum?
Das Einfügen von konstanten Werten in SQL verursacht viele Leistungsprobleme, indem es das Zwischenspeichern von Abfrageplänen verhindert und unnötige Löschungen anderer Abfragen verursacht. Die neue EF Core 8.0-Übersetzung verwendet die SQL Server-Funktion OPENJSON
, um stattdessen die Werte als JSON-Array zu übertragen. Dadurch werden die Leistungsprobleme gelöst, die der vorherigen Technik vorhanden sind. Die OPENJSON
-Funktion ist jedoch in SQL Server 2014 und tiefer nicht verfügbar.
Weitere Informationen zu dieser Änderung finden Sie in diesem Blogbeitrag.
Gegenmaßnahmen
Wenn Ihre Datenbank ein SQL Server 2016 (13.x) oder höher ist, oder wenn Sie Azure SQL verwenden, überprüfen Sie die konfigurierte Kompatibilitätsstufe Ihrer Datenbank mit dem folgenden Befehl:
SELECT name, compatibility_level FROM sys.databases;
Wenn die Kompatibilitätsstufe unter 130 (SQL Server 2016) liegt, sollten Sie diese in einen neueren Wert (Dokumentation) ändern.
Wenn Ihre Datenbankversion tatsächlich älter als SQL Server 2016 ist oder auf eine alte Kompatibilitätsstufe festgelegt ist, die Sie aus irgendeinem Grund nicht ändern können, können Sie EF so konfigurieren, dass sie auf die ältere, pre-8.0 SQL zurückgesetzt wird. In EF 9 können Sie das neu eingeführte TranslateParameterizedCollectionsToConstants verwenden:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer("<CONNECTION STRING>", o => o.TranslateParameterizedCollectionsToConstants())
Wenn Sie EF 8 verwenden, können Sie den gleichen Effekt erzielen, wenn Sie SQL Server verwenden, indem Sie die SQL-Kompatibilitätsstufe von EF konfigurieren:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(@"<CONNECTION STRING>", o => o.UseCompatibilityLevel(120));
Mögliche Abfrageleistungsregressionen um Contains
in LINQ-Abfragen
Nachverfolgung von Issue #32394
Altes Verhalten
EF bot spezielle Unterstützung für LINQ-Abfragen mit dem Contains
-Operator für eine Liste parametrisierter Werte:
var names = new[] { "Blog1", "Blog2" };
var blogs = await context.Blogs
.Where(b => names.Contains(b.Name))
.ToArrayAsync();
Vor EF Core 8.0 hat EF die parametrisierten Werte als Konstanten in die SQL eingefügt:
SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] IN (N'Blog1', N'Blog2')
Neues Verhalten
Ab EF Core 8.0 generiert EF jetzt Folgendes:
SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] IN (
SELECT [n].[value]
FROM OPENJSON(@__names_0) WITH ([value] nvarchar(max) '$') AS [n]
)
Nach der Veröffentlichung von EF 8 wurde jedoch festgestellt, dass das neue SQL in den meisten Fällen zwar effizienter ist, in einigen Fällen jedoch deutlich weniger effizient sein kann und sogar zu Abfragezeitüberschreitungen führen kann.
In diesem Kommentar finden Sie eine Zusammenfassung der Änderung in EF 8, der teilweisen Behebungen in EF 9 und des Plans für EF 10.
Gegenmaßnahmen
In EF 9 können Sie das neu eingeführte TranslateParameterizedCollectionsToConstants verwenden, um die Contains
-Übersetzung für alle Abfragen auf das Verhalten vor 8.0 zurückzusetzen.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer("<CONNECTION STRING>", o => o.TranslateParameterizedCollectionsToConstants())
Wenn Sie EF 8 verwenden, können Sie den gleichen Effekt erzielen, wenn Sie SQL Server verwenden, indem Sie die SQL-Kompatibilitätsstufe von EF konfigurieren:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(@"<CONNECTION STRING>", o => o.UseCompatibilityLevel(120));
Schließlich können Sie die Übersetzung für jede Abfrage einzeln mithilfe von EF.Constant wie folgt steuern:
var blogs = await context.Blogs
.Where(b => EF.Constant(names).Contains(b.Name))
.ToArrayAsync();
Enumerationen in JSON werden standardmäßig als Ganzzahlen statt als Zeichenfolgen gespeichert
Nachverfolgung von Issue #13617
Altes Verhalten
In EF7 werden Enumerationen, die JSON zugeordnet sind, standardmäßig als Zeichenfolgenwerte im JSON-Dokument gespeichert.
Neues Verhalten
Ab EF Core 8.0 ordnet EF jetzt standardmäßig Enumerationen ganzzahligen Werten im JSON-Dokument zu.
Warum?
EF weist Enumerationen standardmäßig einer numerischen Spalte in relationalen Datenbanken zu. Da EF Abfragen unterstützt, bei denen Werte aus JSON mit Werten aus Spalten und Parametern interagieren, ist es wichtig, dass die Werte in JSON mit den Werten in der Nicht-JSON-Spalte übereinstimmen.
Gegenmaßnahmen
Um weiterhin Zeichenfolgen zu verwenden, konfigurieren Sie die Enumerationseigenschaft mit einer Konvertierung. Beispiel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().Property(e => e.Status).HasConversion<string>();
}
Oder für alle Eigenschaften des Enumerationstyps:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties<StatusEnum>().HaveConversion<string>();
}
Änderungen mit mittlerer Auswirkung
SQL Server date
und time
fügen ein Gerüst zu .NET DateOnly
und TimeOnly
hinzu
Nachverfolgung von Issue 24507
Altes Verhalten
Wenn Sie früher ein Gerüstbau für eine SQL Server-Datenbank mit date
- oder time
-Spalten durchgeführt hätten, würde EF Entitätseigenschaften mit den Typen DateTime und TimeSpan erstellen.
Neues Verhalten
Ab EF Core 8.0 werden date
und time
als Gerüst DateOnly und TimeOnly aufgebaut.
Warum?
DateOnly und TimeOnly wurden in .NET 6.0 eingeführt und passen perfekt zur Zuordnung der date- und time-Typen der Datenbank. DateTime enthält insbesondere eine Zeitkomponente, die nicht verwendet wird und zu Verwirrung führen kann, wenn sie date
zugeordnet wird. TimeSpan stellt ein Zeitintervall dar – möglicherweise einschließlich Tage – anstelle einer Tageszeit, zu der ein Ereignis eintritt. Die Verwendung der neuen Typen verhindert Fehler und Verwirrung und bietet Klarheit über die Absicht.
Gegenmaßnahmen
Diese Änderung wirkt sich nur auf Benutzer*innen aus, die ihre Datenbank regelmäßig in einem EF-Codemodell („Database-First“-Flow) neu erstellen.
Es wird empfohlen, auf diese Änderung zu reagieren, indem Sie Ihren Code so ändern, dass die neu erstellten DateOnly und TimeOnly-Typen verwendet werden. Wenn dies jedoch nicht möglich ist, können Sie die Gerüstvorlagen bearbeiten, um das System auf die vorherige Zuordnung zurückzusetzen. Richten Sie dazu die Vorlagen wie auf dieser Seite beschrieben ein. Bearbeiten Sie dann die Datei EntityType.t4
, suchen Sie nach dem Ort, wo die Entitätseigenschaften generiert werden (suchen Sie nach property.ClrType
), und ändern Sie den Code wie folgt:
var clrType = property.GetColumnType() switch
{
"date" when property.ClrType == typeof(DateOnly) => typeof(DateTime),
"date" when property.ClrType == typeof(DateOnly?) => typeof(DateTime?),
"time" when property.ClrType == typeof(TimeOnly) => typeof(TimeSpan),
"time" when property.ClrType == typeof(TimeOnly?) => typeof(TimeSpan?),
_ => property.ClrType
};
usings.AddRange(code.GetRequiredUsings(clrType));
var needsNullable = Options.UseNullableReferenceTypes && property.IsNullable && !clrType.IsValueType;
var needsInitializer = Options.UseNullableReferenceTypes && !property.IsNullable && !clrType.IsValueType;
#>
public <#= code.Reference(clrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
<#
Boolesche Spalten mit einem von der Datenbank generierten Wert werden nicht mehr als Nullwerte zulassend gerüstet
Nachverfolgung von Issue 15070
Altes Verhalten
Zuvor wurden Non-Nullable-bool
-Spalten mit einer Datenbankstandardeinschränkung als Nullwerte zulassende bool?
-Eigenschaften gerüstet.
Neues Verhalten
Ab EF Core 8.0 werden Non-Nullable-bool
-Spalten immer als Non-Nullable-Eigenschaften gerüstet.
Warum?
Der Wert einer bool
-Eigenschaft wird nicht an die Datenbank gesendet, wenn dieser Wert false
ist, was der CLR-Standardeinstellung entspricht. Wenn die Datenbank über einen Standardwert von true
für die Spalte verfügt, ist der Wert in der Datenbank auch dann false
, wenn der Wert der Eigenschaft true
ist. In EF8 ermittelt der Sentinel jedoch, ob eine Eigenschaft einen Wert aufweist, der geändert werden kann. Dies erfolgt automatisch für bool
-Eigenschaften mit einem von der Datenbank generierten Wert von true
, was bedeutet, dass es nicht mehr erforderlich ist, die Eigenschaften als Nullwerte zulassend zu erstellen.
Gegenmaßnahmen
Diese Änderung wirkt sich nur auf Benutzer*innen aus, die ihre Datenbank regelmäßig in einem EF-Codemodell („Database-First“-Flow) neu erstellen.
Es wird empfohlen, auf diese Änderung zu reagieren, indem Sie Ihren Code so ändern, dass die boolesche Non-Nullable-Eigenschaft verwendet. Wenn dies jedoch nicht möglich ist, können Sie die Gerüstvorlagen bearbeiten, um das System auf die vorherige Zuordnung zurückzusetzen. Richten Sie dazu die Vorlagen wie auf dieser Seite beschrieben ein. Bearbeiten Sie dann die Datei EntityType.t4
, suchen Sie nach dem Ort, wo die Entitätseigenschaften generiert werden (suchen Sie nach property.ClrType
), und ändern Sie den Code wie folgt:
#>
var propertyClrType = property.ClrType != typeof(bool)
|| (property.GetDefaultValueSql() == null && property.GetDefaultValue() != null)
? property.ClrType
: typeof(bool?);
#>
public <#= code.Reference(propertyClrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
<#
<#
Änderungen mit geringer Auswirkung
SQLite-Math
-Methoden werden jetzt in SQL übersetzt
Nachverfolgung von Issue #18843
Altes Verhalten
Zuvor wurden nur die Methoden Abs, Max, Min und Round auf Math
in SQL übersetzt. Alle anderen Member würden auf dem Client ausgewertet, wenn sie im endgültigen Select-Ausdruck einer Abfrage erscheinen.
Neues Verhalten
In EF Core 8.0 werden alle Math
-Methoden mit entsprechenden mathematischen SQLite-Funktionen in SQL übersetzt.
Diese mathematischen Funktionen wurden in der nativen SQLite-Bibliothek aktiviert, die wir standardmäßig bereitstellen (durch unsere Abhängigkeit vom NuGet-Paket SQLitePCLRaw.bundle_e_sqlite3). Sie wurden auch in der Bibliothek aktiviert, die von SQLitePCLRaw.bundle_e_sqlcipher bereitgestellt wird. Wenn Sie eine dieser Bibliotheken verwenden, sollte Ihre Anwendung von dieser Änderung nicht betroffen sein.
Es besteht jedoch die Möglichkeit, dass Anwendungen, welche die native SQLite-Bibliothek auf andere Weise einschließen, die mathematischen Funktionen möglicherweise nicht aktivieren. In diesen Fällen werden die Math
-Methoden in SQL übersetzt und treffen bei der Ausführung auf den Fehler keine solche Funktion.
Warum?
SQLite hat integrierte mathematische Funktionen in Version 3.35.0 hinzugefügt. Auch wenn sie standardmäßig deaktiviert sind, sind sie unterdessen so weit verbreitet, dass wir beschlossen haben, Standardübersetzungen für sie in unserem EF Core SQLite-Anbieter bereitzustellen.
Wir haben auch mit Eric Sink am SQLitePCLRaw-Projekt zusammengearbeitet, um mathematische Funktionen in allen nativen SQLite-Bibliotheken zu aktivieren, die im Rahmen dieses Projekts bereitgestellt werden.
Gegenmaßnahmen
Die einfachste Möglichkeit zum Beheben von Unterbrechungen ist es, wenn möglich die mathematische Funktion in der nativen SQLite-Bibliothek zu aktivieren, indem Sie die Kompilierzeitoption SQLITE_ENABLE_MATH_FUNCTIONS angeben.
Wenn Sie nicht die Kompilierung der nativen Bibliothek steuern, können Sie Unterbrechungen auch beheben, indem Sie die Funktionen zur Laufzeit mithilfe der Microsoft.Data.Sqlite-APIs selber erstellen.
sqliteConnection
.CreateFunction<double, double, double>(
"pow",
Math.Pow,
isDeterministic: true);
Alternativ können Sie die Clientauswertung erzwingen, indem Sie den Select-Ausdruck in zwei durch AsEnumerable
getrennte Teile trennen.
// Before
var query = dbContext.Cylinders
.Select(
c => new
{
Id = c.Id
// May throw "no such function: pow"
Volume = Math.PI * Math.Pow(c.Radius, 2) * c.Height
});
// After
var query = dbContext.Cylinders
// Select the properties you'll need from the database
.Select(
c => new
{
c.Id,
c.Radius,
c.Height
})
// Switch to client-eval
.AsEnumerable()
// Select the final results
.Select(
c => new
{
Id = c.Id,
Volume = Math.PI * Math.Pow(c.Radius, 2) * c.Height
});
ITypeBase ersetzt IEntityType in einigen APIs
Nachverfolgung von Issue 13947
Altes Verhalten
Zuvor waren alle zugeordneten Strukturtypen Entitätstypen.
Neues Verhalten
Mit der Einführung komplexer Typen in EF8 verwenden einige APIs, die zuvor einen IEntityType
verwendeten, jetzt ITypeBase
, damit die APIs entweder mit Entitätstypen oder komplexen Typen verwendet werden können. Dies umfasst u. a.:
IProperty.DeclaringEntityType
ist jetzt veraltet undIProperty.DeclaringType
sollte stattdessen verwendet werden.IEntityTypeIgnoredConvention
ist jetzt veraltet undITypeIgnoredConvention
sollte stattdessen verwendet werden.IValueGeneratorSelector.Select
akzeptiert nun eineITypeBase
, die einIEntityType
sein kann, aber nicht sein muss.
Warum?
Mit der Einführung komplexer Typen in EF8 können diese APIs entweder mit IEntityType
oder mit IComplexType
verwendet werden.
Gegenmaßnahmen
Die alten APIs sind veraltet, werden jedoch erst nach EF10 entfernt. Der Code sollte aktualisiert werden, um die neuen APIs schnellstmöglich zu verwenden.
ValueConverter- und ValueComparer-Ausdrücke müssen öffentliche APIs für das kompilierte Modell verwenden.
Nachverfolgung von Issue 24896
Altes Verhalten
Zuvor waren ValueConverter
- und ValueComparer
-Definitionen nicht im kompilierten Modell enthalten und konnten somit beliebigen Code enthalten.
Neues Verhalten
EF extrahiert nun die Ausdrücke aus den ValueConverter
- und ValueComparer
-Objekten und schließt diese C# in das kompilierte Modell ein. Das bedeutet, dass diese Ausdrücke nur öffentliche API verwenden dürfen.
Warum?
Das EF-Team verschiebt schrittweise mehr Konstrukte in das kompilierte Modell, um in Zukunft die Verwendung von EF Core mit AOT zu unterstützen.
Gegenmaßnahmen
Machen Sie die APIs, die vom Comparer verwendet werden, öffentlich. Betrachten Sie als Beispiel diesen einfachen Konverter:
public class MyValueConverter : ValueConverter<string, byte[]>
{
public MyValueConverter()
: base(v => ConvertToBytes(v), v => ConvertToString(v))
{
}
private static string ConvertToString(byte[] bytes)
=> ""; // ... TODO: Conversion code
private static byte[] ConvertToBytes(string chars)
=> Array.Empty<byte>(); // ... TODO: Conversion code
}
Um diesen Konverter in einem kompilierten Modell mit EF8 zu verwenden, müssen die ConvertToString
- und ConvertToBytes
-Methoden öffentlich gemacht werden. Beispiel:
public class MyValueConverter : ValueConverter<string, byte[]>
{
public MyValueConverter()
: base(v => ConvertToBytes(v), v => ConvertToString(v))
{
}
public static string ConvertToString(byte[] bytes)
=> ""; // ... TODO: Conversion code
public static byte[] ConvertToBytes(string chars)
=> Array.Empty<byte>(); // ... TODO: Conversion code
}
ExcludeFromMigrations schließt andere Tabellen in einer TPC-Hierarchie nicht mehr aus
Nachverfolgung von Issue 30079
Altes Verhalten
Zuvor hat die Verwendung von ExcludeFromMigrations
in einer Tabelle in einer TPC-Hierarchie auch andere Tabellen in der Hierarchie ausgeschlossen.
Neues Verhalten
Ab EF Core 8.0 wirkt sich ExcludeFromMigrations
nicht auf andere Tabellen aus.
Warum?
Das alte Verhalten war ein Fehler und verhinderte, dass Migrationen verwendet wurden, um Hierarchien über Projekte hinweg zu verwalten.
Gegenmaßnahmen
Verwenden Sie ExcludeFromMigrations
explizit für jede andere Tabelle, die ausgeschlossen werden soll.
Ganzzahlige Schlüssel ohne Schatten werden in Cosmos-Dokumenten beibehalten
Nachverfolgung von Issue 31664
Altes Verhalten
Bisher wurden ganzzahlige Eigenschaften ohne Schatten, die die Kriterien für eine synthetisierte Schlüsseleigenschaft erfüllen, nicht in das JSON-Dokument übernommen, sondern auf dem Weg neu synthetisiert.
Neues Verhalten
Ab EF Core 8.0 werden diese Eigenschaften nun beibehalten.
Warum?
Das alte Verhalten war ein Fehler und verhinderte, dass Eigenschaften, die den synthetisierten Schlüsselkriterien entsprechen, auf Cosmos beibehalten wurden.
Gegenmaßnahmen
Schließen Sie die Eigenschaft aus dem Modell aus, wenn der Wert nicht beibehalten werden soll.
Außerdem können Sie dieses Verhalten vollständig deaktivieren, indem Sie den Microsoft.EntityFrameworkCore.Issue31664
AppContext-Schalter auf true
setzen, siehe AppContext für Bibliothekskonsumenten für weitere Details.
AppContext.SetSwitch("Microsoft.EntityFrameworkCore.Issue31664", isEnabled: true);
Relationales Modell wird im kompilierten Modell generiert
Nachverfolgung von Issue 24896
Altes Verhalten
Zuvor wurde das relationale Modell zur Laufzeit auch bei Verwendung eines kompilierten Modells berechnet.
Neues Verhalten
Ab EF Core 8.0 ist das relationale Modell Teil des generierten kompilierten Modells. Bei besonders großen Modellen kann die generierte Datei jedoch nicht kompiliert werden.
Warum?
Dies wurde gemacht, um die Startzeit weiter zu verbessern.
Gegenmaßnahmen
Bearbeiten Sie die generierte *ModelBuilder.cs
-Datei und entfernen Sie die Zeile AddRuntimeAnnotation("Relational:RelationalModel", CreateRelationalModel());
sowie die Methode CreateRelationalModel()
.
Gerüstbau kann verschiedene Navigationsnamen generieren
Nachverfolgung von Issue 27832
Altes Verhalten
Wenn früher ein Gerüst aus einem DbContext
-Element und Entitätstypen aus einer vorhandenen Datenbank erstellt wurden, wurden die Navigationsnamen für Beziehungen manchmal von einem gemeinsamen Präfix mehrerer Fremdschlüsselspaltennamen abgeleitet.
Neues Verhalten
Ab EF Core 8.0 werden gemeinsame Präfixe von Spaltennamen aus einem zusammengesetzten Fremdschlüssel nicht mehr zum Generieren von Navigationsnamen verwendet.
Warum?
Dies ist eine undurchsichtige Benennungsregel, die manchmal sehr ungünstige Namen wie S
, Student_
oder sogar nur_
generiert. Ohne diese Regel werden keine seltsamen Namen mehr generiert, und die Namenskonventionen für Navigationen werden ebenfalls vereinfacht, wodurch leichter zu verstehen und vorherzusagen ist, welche Namen generiert werden.
Gegenmaßnahmen
Die EF Core Power Tools bieten die Möglichkeit, Navigationen weiterhin auf die alte Weise zu generieren. Alternativ kann der generierte Code mithilfe von T4-Vorlagen vollständig angepasst werden. Dies kann als Beispiel für die Fremdschlüsseleigenschaften von Gerüstbaubeziehungen verwendet werden und jede beliebige Regel verwenden, die für Ihren Code geeignet ist, um die von Ihnen benötigten Navigationsnamen zu generieren.
Für Diskriminatoren gilt jetzt eine maximale Länge
Nachverfolgung von Issue 10691
Altes Verhalten
Zuvor wurden Diskriminatorspalten, die für TPH-Vererbungsmapping erstellt wurden, in SQL Server/Azure SQL als nvarchar(max)
oder in anderen Datenbanken als äquivalenter ungebundener Zeichenfolgentyp konfiguriert.
Neues Verhalten
Ab EF Core 8.0 werden Diskriminatorspalten mit einer maximalen Länge erstellt, die alle bekannten Diskriminatorwerte abdeckt. EF generiert eine Migration, um diese Änderung vorzunehmen. Wenn die Diskriminatorspalte jedoch in irgendeiner Weise eingeschränkt ist – z. B. als Teil eines Indizes –, kann bei der durch Migrationen erstellte AlterColumn
-Spalte ein Fehler auftreten.
Warum?
nvarchar(max)
-Spalten sind ineffizient und unnötig, wenn die Länge aller möglichen Werte bekannt ist.
Gegenmaßnahmen
Die Spaltengröße kann explizit als ungebunden festgelegt werden:
modelBuilder.Entity<Foo>()
.Property<string>("Discriminator")
.HasMaxLength(-1);
Beim Vergleich von SQL Server-Schlüsselwerten wird die Groß-/Kleinschreibung nicht berücksichtigt
Nachverfolgung von Issue 27526
Altes Verhalten
Bisher wurden beim Nachverfolgen von Entitäten mit Zeichenfolgenschlüsseln mit den SQL Server/Azure SQL-Datenbankanbietern die Schlüsselwerte mithilfe der standardmäßigen ordinalen Vergleichsfunktion von .NET verglichen, die Groß-/Kleinschreibung berücksichtigt.
Neues Verhalten
Ab EF Core 8.0 werden SQL Server/Azure SQL-Zeichenfolgenschlüsselwerte mithilfe der standardmäßigen ordinalen Vergleichsfunktion von .NET verglichen, die Groß-/Kleinschreibung ignoriert.
Warum?
Standardmäßig unterscheidet SQL Server bei Vergleichen von Fremdschlüsselwerten mit Prinzipalschlüsselwerten nicht zwischen Groß-/Kleinschreibung. Das bedeutet: Wenn EF die Groß-/Kleinschreibung beim Vergleichen berücksichtigt, kann es sein, dass ein Fremdschlüssel nicht mit einem Prinzipalschlüssel verbunden wird, obwohl dies der Fall sein sollte.
Gegenmaßnahmen
Vergleiche mit Berücksichtigung von Groß- und Kleinschreibung können durch Festlegen eines benutzerdefinierten ValueComparer
verwendet werden. Beispiel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var comparer = new ValueComparer<string>(
(l, r) => string.Equals(l, r, StringComparison.Ordinal),
v => v.GetHashCode(),
v => v);
modelBuilder.Entity<Blog>()
.Property(e => e.Id)
.Metadata.SetValueComparer(comparer);
modelBuilder.Entity<Post>(
b =>
{
b.Property(e => e.Id).Metadata.SetValueComparer(comparer);
b.Property(e => e.BlogId).Metadata.SetValueComparer(comparer);
});
}
Mehrere AddDbContext-Aufrufe werden in anderer Reihenfolge angewendet
Nachverfolgung von Issue 32518
Altes Verhalten
Bislang galt: Wenn mehrere Aufrufe für AddDbContext
, AddDbContextPool
, AddDbContextFactory
oder AddPooledDbContextFactor
mit dem gleichen Kontexttyp, aber in Konflikt stehender Konfiguration ausgeführt wurden, hatte der erste Vorrang.
Neues Verhalten
Ab EF Core 8.0 hat die Konfiguration aus dem letzten Aufruf Vorrang.
Warum?
Dies wurde geändert, um mit der neuen Methode ConfigureDbContext
konsistent zu sein, mit der eine Konfiguration entweder vor oder nach den Methoden vom Typ Add*
hinzugefügt werden kann.
Gegenmaßnahmen
Kehren Sie die Reihenfolge von Add*
-Aufrufen um.
EntityTypeAttributeConventionBase wurde durch TypeAttributeConventionBase ersetzt
Neues Verhalten
In EF Core 8.0 wurde EntityTypeAttributeConventionBase
in TypeAttributeConventionBase
umbenannt.
Warum?
TypeAttributeConventionBase
gibt die Funktion besser wieder, da es jetzt für komplexe Typen und Entitätstypen verwendet werden kann.
Gegenmaßnahmen
Ersetzen Sie Instanzen von EntityTypeAttributeConventionBase
durch TypeAttributeConventionBase
.