Breaking Changes in EF Core 3.x
Die folgenden API-Änderungen und Behavior Changes können dazu führen, dass vorhandene Anwendungen nach einem Upgrade auf 3.x nicht mehr funktionieren. Änderungen, die sich voraussichtlich nur auf Datenbankanbieter auswirken, sind unter Änderungen mit Auswirkungen auf den Anbieter dokumentiert.
Zusammenfassung
Änderungen mit hoher Auswirkung
LINQ-Abfragen werden nicht mehr auf dem Client ausgewertet
Issue #14935Siehe auch Issue #12795
Altes Verhalten
Vor Version 3.0 wurden in EF Core automatisch Ausdrücke auf dem Client ausgewertet, wenn diese Teil einer Abfrage waren und nicht in SQL oder in einen Parameter konvertiert werden konnten. Wenn potenziell aufwendige Ausdrücke auf dem Client ausgewertet werden sollten, wurde standardmäßig nur eine Warnung ausgelöst.
Neues Verhalten
Ab Version 3.0 können in EF Core nur Ausdrücke der obersten Projektion (dem letzten Select()
-Aufruf in der Abfrage) auf dem Client ausgewertet werden.
Wenn Ausdrücke in einem anderen Teil der Abfrage nicht in SQL oder in einen Parameter konvertiert werden können, wird eine Ausnahme ausgelöst.
Warum?
Die automatische Auswertung von Abfragen auf Clients hat zur Folge, dass viele Abfragen auch dann ausgeführt werden, wenn wichtige Teile nicht übersetzt werden können.
Dieses Verhalten kann zu unerwartetem und potenziell schädlichem Verhalten führen, das möglicherweise erst in der Produktionsphase erkannt wird.
Eine nicht übersetzbare Bedingung in einem Where()
-Aufruf kann sich beispielsweise so auswirken, dass alle Zeilen einer Tabelle vom Datenbankserver übertragen werden und der Filter auf dem Client angewendet wird.
Wenn die Tabelle in der Entwicklungsphase nur wenige Zeilen enthält, ist es gut möglich, dass diese Situation unerkannt bleibt. Wenn die Anwendung jedoch in die Produktionsumgebung überführt wird, enthält die Tabelle möglicherweise Millionen von Zeilen, was zu schwerwiegenden Konsequenzen führen kann.
Wie die Erfahrung in der Entwicklungsphase gezeigt hat, können auch Warnungen, die bei Auswertungen auf dem Client ausgelöst werden, dieses Problem nicht lösen, da sie sich leicht ignorieren lassen.
Die automatische Auswertung auf Clients kann darüber hinaus zu Problemen führen, bei denen die Verbesserung der Abfrageübersetzung für bestimmte Ausdrücke zu unbeabsichtigten Breaking Changes zwischen Releases führt.
Gegenmaßnahmen
Wenn sich eine Abfrage nicht vollständig übersetzen lässt, habe Sie zwei Möglichkeiten: Schreiben Sie sie entweder um, oder setzen Sie alternativ AsEnumerable()
, ToList()
oder ähnliche Methoden ein, um Daten wieder zurück an den Client zu übertragen, wo sie mit LINQ to Objects weiterverarbeitet werden können.
Änderungen mit mittlerer Auswirkung
Entity Framework Core ist nicht mehr Bestandteil des gemeinsam verwendeten ASP.NET Core-Frameworks
Altes Verhalten
Vor Version 3.0 von ASP.NET Core wurden EF Core und einige der zugehörigen Datenanbieter wie SQL Server eingeschlossen, wenn Microsoft.AspNetCore.App
oder Microsoft.AspNetCore.All
ein Paketverweis hinzugefügt wurde.
Neues Verhalten
Ab Version 3.0 enthält das gemeinsam verwendete ASP.NET Core-Framework weder EF Core noch zugehörige Datenanbieter.
Warum?
Vor dieser Änderung konnte EF Core auf unterschiedliche Arten bezogen werden. Diese richteten sich danach, ob ASP.NET Core und SQL Server oder ein anderes Zielframework für die Anwendung vorgesehen war. Bei einem Upgrade von ASP.NET Core wurde zudem ein Upgrade von EF Core und des SQL Server-Anbieters erzwungen, was nicht in allen Fällen gewünscht war.
Durch die eingeführte Änderung wird EF Core unter allen Anbietern, unterstützten .NET-Implementierungen und Anwendungstypen auf dieselbe Weise bezogen. Entwickler können nun außerdem genau festlegen, wann für EF Core und zugehörige Datenanbieter ein Upgrade durchgeführt werden soll.
Gegenmaßnahmen
Wenn Sie EF Core in einer Anwendung unter ASP.NET Core 3.0 oder in einer anderen unterstützten Anwendung verwenden möchten, müssen Sie dem EF Core-Datenbankanbieter, der für die Anwendung genutzt werden soll, explizit einen Paketverweis hinzufügen.
Das EF Core-Befehlszeilentool (dotnet ef) ist nicht mehr Bestandteil des .NET Core SDK
Altes Verhalten
Vor Version 3.0 war das dotnet ef
-Tool im .NET Core SDK enthalten und konnte von der Befehlszeile aus ohne zusätzliche Schritte in einem beliebigen Projekt verwendet werden.
Neues Verhalten
Ab Version 3.0 ist das dotnet ef
-Tool nicht mehr im .NET SDK enthalten und muss deshalb vor der Verwendung explizit als lokales oder globales Tool installiert werden.
Warum?
Durch diese Änderung kann dotnet ef
als reguläres .NET-CLI-Tool für NuGet verteilt und aktualisiert werden und entspricht damit dem üblichen Verfahren, dass EF Core 3.0 ebenfalls immer als NuGet-Paket verteilt wird.
Gegenmaßnahmen
Um Migrationen zu verwalten oder ein Gerüst für DbContext
zu erstellen, installieren Sie dotnet-ef
als globales Tool:
dotnet tool install --global dotnet-ef
Eine Verwendung als lokales Tool ist ebenfalls möglich, wenn Sie die Abhängigkeiten eines Projekts wiederherstellen, das das Tool mithilfe einer Toolmanifestdatei als Toolabhängigkeit deklariert.
Änderungen mit geringer Auswirkung
FromSql, ExecuteSql und ExecuteSqlAsync wurden umbenannt
Wichtig
ExecuteSqlCommand
und ExecuteSqlCommandAsync
sind veraltet. Verwenden Sie diese Methoden stattdessen.
Altes Verhalten
Vor Version 3.0 wurden in EF Core diese Methodennamen überladen, um entweder mit einer normalen Zeichenfolge oder einer Zeichenfolge, die in SQL und Parameter interpoliert werden sollte, zu arbeiten.
Neues Verhalten
Ab Version 3.0 verwenden Sie in EF Core FromSqlRaw
, ExecuteSqlRaw
und ExecuteSqlRawAsync
, um eine parametrisierte Abfrage zu erstellen, bei der die Parameter einzeln aus der Abfragezeichenfolge übergeben werden.
Beispiel:
context.Products.FromSqlRaw(
"SELECT * FROM Products WHERE Name = {0}",
product.Name);
Verwenden Sie FromSqlInterpolated
, ExecuteSqlInterpolated
und ExecuteSqlInterpolatedAsync
, um eine parametrisierte Abfrage zu erstellen, bei der die Parameter als Teil einer interpolierten Abfragezeichenfolge übergeben werden.
Beispiel:
context.Products.FromSqlInterpolated(
$"SELECT * FROM Products WHERE Name = {product.Name}");
Beachten Sie, dass die beiden oben genannten Abfragen dieselbe parametrisierte SQL mit denselben SQL-Parametern erzeugen.
Warum?
Bei Methodenüberladungen wie dieser wird sehr leicht versehentlich die Methode für unformatierte Zeichenfolgen aufgerufen, wenn eigentlich beabsichtigt war, die Methode für interpolierte Zeichenfolgen aufzurufen (und umgekehrt). Dadurch werden Abfragen möglicherweise nicht parametrisiert, obwohl dies der Fall sein sollte.
Gegenmaßnahmen
Verwenden Sie ab sofort die neuen Methodennamen.
FromSql-Methode kann nicht zusammengesetzt werden, wenn sie mit einer gespeicherten Prozedur verwendet wird
Altes Verhalten
Vor EF Core 3.0 versuchte die FromSql-Methode, zu ermitteln, ob das übergebene SQL zusammengesetzt werden konnte. Eine Clientauswertung wurde durchgeführt, wenn das SQL wie eine gespeicherte Prozedur nicht zusammensetzbar war. Die folgende Abfrage funktionierte, indem die gespeicherte Prozedur auf dem Server und die FirstOrDefault-Methode auf dem Client ausgeführt wurde.
context.Products.FromSqlRaw("[dbo].[Ten Most Expensive Products]").FirstOrDefault();
Neues Verhalten
Ab EF Core 3.0 versucht EF Core nicht mehr, das SQL zu analysieren. Wenn Sie also nach FromSqlRaw/FromSqlInterpolated zusammensetzen, setzt EF Core das SQL zusammen, indem eine Unterabfrage ausgelöst wird. Wenn Sie eine gespeicherte Prozedur mit Zusammensetzung verwenden, wird eine Ausnahme für ungültige SQL-Syntax ausgelöst.
Warum?
EF Core 3.0 unterstützt die automatische Clientauswertung nicht, da diese wie hier erläutert fehleranfällig war.
Gegenmaßnahmen
Wenn Sie eine gespeicherte Prozedur in FromSqlRaw/FromSqlInterpolated verwenden, wissen Sie, dass diese nicht zusammengesetzt werden kann. Daher können Sie AsEnumerable
/AsAsyncEnumerable
direkt nach dem FromSql-Methodenaufruf hinzufügen, um Zusammensetzungen auf dem Server zu vermeiden.
context.Products.FromSqlRaw("[dbo].[Ten Most Expensive Products]").AsEnumerable().FirstOrDefault();
FromSql-Methoden können nur für die Stammelemente der Abfrage angegeben werden
Altes Verhalten
Vor EF Core 3.0 konnte die FromSql
-Methode an einer beliebigen Stelle in der Abfrage angegeben werden.
Neues Verhalten
Ab EF Core 3.0 können die neuen Methoden FromSqlRaw
und FromSqlInterpolated
(durch die FromSql
ersetzt wird) nur für Stammelemente von Abfragen angegeben werden, d.h. direkt für das DbSet<>
. Der Versuch, sie an anderer Stelle anzugeben, führt zu einem Kompilierungsfehler.
Warum?
Die Angabe von FromSql
an einer anderen Stelle als für ein DbSet
erbrachte keine zusätzliche Bedeutung und keinen Mehrwert, sondern konnte in bestimmten Szenarien zu Mehrdeutigkeiten führen.
Gegenmaßnahmen
FromSql
Aufrufe sollten so verschoben, dass sie direkt für das zugehörige DbSet
gelten.
Abfragen ohne Nachverfolgung führen keine Identitätsauflösung mehr durch
Altes Verhalten
Vor EF Core 3.0 wurde dieselbe Entitätsinstanz für jedes Vorkommen einer Entität mit einem bestimmten Typ und einer bestimmten ID verwendet. Dies entspricht dem Verhalten von Abfragen zu Nachverfolgungen. Diese Abfrage zum Beispiel:
var results = context.Products.Include(e => e.Category).AsNoTracking().ToList();
gibt dieselbe Category
-Instanz für jedes Product
zurück, das der bestimmten Kategorie zugeordnet ist.
Neues Verhalten
Ab EF Core 3.0 werden unterschiedliche Entitätsinstanzen erstellt, wenn eine Entität mit einem bestimmten Typ und einer bestimmten ID an verschiedenen Stellen im zurückgegebenen Diagramm gefunden wird. Die Abfrage oben gibt beispielsweise nun eine neue Category
-Instanz für jedes Product
zurück, auch wenn zwei Produkte derselben Kategorie zugeordnet sind.
Warum?
Die Identitätsauflösung (d. h. Feststellen, dass eine Entität über denselben Typ und die dieselbe ID wie die zuvor aufgetretene Entität verfügt) fügt zusätzlichen Aufwand für Leistung und Arbeitsspeicher hinzu. Dies widerspricht normalerweise dem Grund, warum in erster Linie Abfragen ohne Nachverfolgung verwendet werden. Obwohl die Identitätsauflösung manchmal nützlich sein kann, wird sie nicht benötigt, wenn die Entitäten serialisiert und an den Client gesendet werden, was manchmal für Abfragen ohne Nachverfolgung der Fall ist.
Gegenmaßnahmen
Verwenden Sie eine Nachverfolgungsabfrage, falls die Auflösung erforderlich ist.
Temporäre Schlüsselwerte werden nicht mehr Entitätsinstanzen zugewiesen
Altes Verhalten
Vor Version 3.0 wurden in EF Core allen Schlüsseleigenschaften temporäre Werte zugewiesen. Später wurden den Eigenschaften dann die tatsächlichen Werte zugewiesen, die von der Datenbank generiert wurden. Die temporären Werte waren in der Regel große negative Zahlen.
Neues Verhalten
Ab Version 3.0 wird in EF Core der temporäre Wert als Teil der Überwachungsinformationen der Entität gespeichert. Die Schlüsseleigenschaft selbst bleibt unverändert.
Warum?
Diese Änderung wurde vorgenommen, damit temporäre Schlüsselwerte nicht fälschlicherweise dauerhaft gespeichert werden, wenn eine Entität, die vorher von einer DbContext
-Instanz überwacht wurde, in eine andere DbContext
-Instanz verschoben wird.
Gegenmaßnahmen
Anwendungen, die Primärschlüsselwerte Fremdschlüsseln zuweisen, um Zuordnungen zwischen Entitäten zu erstellen, können vom alten Verhalten abhängig sein, wenn Primärschlüssel vom Speicher generiert werden und zu Entitäten im Added
-Zustand gehören.
Sie können dies wie folgt vermeiden:
- Verwenden Sie keine vom Speicher generierten Schlüssel.
- Legen Sie zum Erstellen von Zuordnungen keine Fremdschlüsselwerte, sondern Navigationseigenschaften fest.
- Rufen Sie die tatsächlichen temporären Schlüsselwerte aus den Überwachungsinformationen der Entität ab.
context.Entry(blog).Property(e => e.Id).CurrentValue
gibt beispielsweise den temporären Wert zurück, obwohlblog.Id
noch nicht festgelegt wurde.
DetectChanges berücksichtigt vom Speicher generierte Schlüsselwerte
Altes Verhalten
Vor Version 3.0 wurde in EF Core eine nicht überwachte Entität, die von DetectChanges
gefunden wurde, im Added
-Zustand überwacht und als neue Zeile eingefügt, sobald SaveChanges
aufgerufen wurde.
Neues Verhalten
Ab Version 3.0 wird in EF Core die Entität im Modified
-Zustand überwacht, wenn für diese generierte Schlüsselwerte verwendet werden und ein Schlüsselwert festgelegt wird.
Es wird also davon ausgegangen, dass eine Zeile für die Entität vorhanden ist. Wenn SaveChanges
aufgerufen wird, wird die Zeile außerdem aktualisiert.
Falls kein Schlüsselwert festgelegt ist oder der Entitätstyp keine generierten Schlüssel verwendet, wird die neue Entität wie in früheren Version weiterhin im Added
-Zustand überwacht.
Warum?
Diese Änderung wurde vorgenommen, um bei der Verwendung von speichergenerierten Schlüsseln die Arbeit mit nicht verbundenen Entitätsgraphen einfacher und konsistenter zu gestalten.
Gegenmaßnahmen
Diese Änderung kann dazu führen, dass eine Anwendung nicht mehr funktioniert. Dieser Fall tritt ein, wenn für Entitätstypen generierte Schlüssel vorgesehen sind, dann jedoch explizit Schlüsselwerte für neue Instanzen festgelegt werden. Sie können dieses Problem vermeiden, indem Sie explizit festlegen, dass für Schlüsseleigenschaften keine generierten Werte verwendet werden sollen. Dies ist beispielsweise mit der Fluent-API möglich:
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.ValueGeneratedNever();
Alternativ bieten sich auch Datenanmerkungen an:
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
Kaskadierende DELETE-Anweisungen werden standardmäßig sofort ausgeführt
Altes Verhalten
Vor Version 3.0 wurden in EF Core kaskadierenden Aktionen (Löschen von abhängigen Entitäten nach dem Löschen eines erforderlichen Prinzipals oder nach dem Aufheben einer Beziehung zum erforderlichen Prinzipal) erst ausgeführt, wenn SaveChanges aufgerufen wurde.
Neues Verhalten
Ab Version 3.0 werden in EF Core kaskadierende Aktionen angewendet, sobald die auslösende Bedingung erkannt wird.
Wenn beispielsweise context.Remove()
zum Löschen einer Prinzipalentität aufgerufen wird, führt das dazu, dass auch alle zugehörigen erforderlichen Entitäten, die überwacht werden, sofort auf Deleted
festgelegt werden.
Warum?
Diese Änderung wurde vorgenommen, um die Arbeit mit Datenbindungen und Überwachungsszenarios zu vereinfachen, in denen bekannt sein muss, welche Entitäten vor dem Aufruf von SaveChanges
gelöscht werden.
Gegenmaßnahmen
Sie können das vorherige Verhalten wiederherstellen, indem Sie für context.ChangeTracker
die entsprechenden Einstellungen vornehmen.
Beispiel:
context.ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;
Eager Loading von verwandten Entitäten erfolgt nun in einer einzelnen Abfrage
Altes Verhalten
Vor Version 3.0 löste Eager Loading von Sammlungsnavigationen über Include
-Operatoren die Generierung mehrerer Abfragen auf der relationalen Datenbank aus. Eine Abfrage für jeden verwandten Entitätstyp.
Neues Verhalten
Ab Version 3.0 generiert EF Core eine einzelne Abfrage mit JOINs für relationale Datenbanken.
Warum?
Das Ausgeben mehrerer Abfragen zum Implementieren einer einzelnen LINQ-Abfrage führte zu einer Vielzahl von Problemen, darunter Leistungsbeeinträchtigungen, da mehrere Datenbankroundtrips erforderlich waren, und Datenkohärenzprobleme, da jede Abfrage einen anderen Zustand der Datenbank beobachten könnte.
Gegenmaßnahmen
Zwar ist dies technisch gesehen kein Breaking Change, jedoch könnte es erhebliche Auswirkungen auf die Leistung der Anwendung haben, wenn eine einzelne Abfrage eine große Anzahl von Include
-Operatoren für Sammlungsnavigationen enthält. Weitere Informationen sowie Informationen zum Umschreiben von Abfragen auf eine effizientere Weise finden Sie in diesem Kommentar.
**
DeleteBehavior.Restrict verfügt über eine übersichtlichere Semantik
Altes Verhalten
Vor Version 3.0 wurden mit DeleteBehavior.Restrict
Fremdschlüssel in der Datenbank mit Restrict
-Semantik erstellt, es wurden aber auch unvorhergesehen interne Fixups geändert.
Neues Verhalten
Ab Version 3.0 wird mit DeleteBehavior.Restrict
sichergestellt, dass Fremdschlüssel mit Restrict
-Semantik erstellt werden – d. h. ohne Überlappungen; ausgenommen bei Einschränkungsverletzungen – und ohne Auswirkungen auf EF-interne Fixups.
Warum?
Diese Änderung wurde vorgenommen, um die Benutzerfreundlichkeit bei intuitiver Verwendung von DeleteBehavior
ohne unerwartete Nebeneffekte zu verbessern.
Gegenmaßnahmen
Sie können das vorherige Verhalten wiederherstellen, indem Sie DeleteBehavior.ClientNoAction
verwenden.
Abfragetypen werden mit Entitätstypen zusammengeführt
Altes Verhalten
Vor Version 3.0 wurden in EF Core Abfragetypen dazu verwendet, Daten abzufragen, in denen kein Primärschlüssel auf strukturierte Weise festgelegt war. Abfragetypen wurden also für die Zuordnung von Entitätstypen ohne Schlüssel verwendet (in der Regel in einer Sicht, in einigen Fällen aber auch in einer Tabelle), während reguläre Entitätstypen für einen verfügbaren Schlüssel verwendet wurden (in der Regel in einer Tabelle, in einigen Fällen aber auch in einer Sicht).
Neues Verhalten
Aus einem Abfragetyp wird nun einfach ein Entitätstyp ohne Primärschlüssel. Entitätstypen ohne Schlüssel besitzen die gleiche Funktionalität wie Abfragetypen in früheren Versionen.
Warum?
Diese Änderung wurde vorgenommen, um Unklarheiten bei der Verwendung von Abfragetypen zu beseitigen. Abfragetypen sind schlüssellose Entitätstypen, die grundsätzlich schreibgeschützt sind. Sie sollten allerdings nicht allein wegen des Schreibschutzes verwendet werden. Des Weiteren werden sie oft Sichten zugeordnet, was aber nur daran liegt, dass für Letztere häufig keine Schlüssel definiert werden.
Gegenmaßnahmen
Die folgenden Teile der API sind durch die Änderungen veraltet:
ModelBuilder.Query<>()
: Rufen Sie stattdessenModelBuilder.Entity<>().HasNoKey()
auf, um einen schlüssellosen Entitätstyp festzulegen. Dieses Verhalten wird nach wie vor nicht konventionsgemäß festgelegt, um Fehlkonfigurationen zu vermeiden, wenn ein Primärschlüssel erwartet wird, jedoch nicht mit der Konvention übereinstimmt.DbQuery<>
: Verwenden Sie stattdessenDbSet<>
.DbContext.Query<>()
: Verwenden Sie stattdessenDbContext.Set<>()
.IQueryTypeConfiguration<TQuery>
: Verwenden Sie stattdessenIEntityTypeConfiguration<TEntity>
.
Hinweis
Aufgrund eines Problems in Version 3.x beim Abfragen von schlüssellosen Entitäten, bei denen alle Eigenschaften auf null
festgelegt sind, wird anstelle einer Entität ein null
zurückgegeben. Wenn dieses Problem auf Ihr Szenario zutrifft, fügen Sie außerdem eine Logik zur Verarbeitung von null
in Ergebnissen hinzu.
Die Konfigurations-API für Beziehungen abhängiger (owned) Typen wurde geändert
Issue #12444Issue #9148Issue #14153
Altes Verhalten
Vor Version 3.0 wurde in EF Core die abhängige Beziehung direkt nach dem Aufruf von OwnsOne
oder OwnsMany
konfiguriert.
Neues Verhalten
Ab Version 3.0 von EF Core ist eine Fluent-API verfügbar, mit der über WithOwner()
eine Navigationseigenschaft für den Besitzer konfiguriert werden kann.
Beispiel:
modelBuilder.Entity<Order>.OwnsOne(e => e.Details).WithOwner(e => e.Order);
Die Konfiguration für die Beziehung zwischen Besitzertyp und abhängigem Typ sollte nun ähnlich wie bei der Konfiguration anderer Beziehungen nach WithOwner()
verkettet werden.
Die Konfiguration für den abhängigen Typ wird jedoch weiterhin nach OwnsOne()/OwnsMany()
verkettet.
Beispiel:
modelBuilder.Entity<Order>.OwnsOne(e => e.Details, eb =>
{
eb.WithOwner()
.HasForeignKey(e => e.AlternateId)
.HasConstraintName("FK_OrderDetails");
eb.ToTable("OrderDetails");
eb.HasKey(e => e.AlternateId);
eb.HasIndex(e => e.Id);
eb.HasOne(e => e.Customer).WithOne();
eb.HasData(
new OrderDetails
{
AlternateId = 1,
Id = -1
});
});
Wenn Sie zusätzlich Entity()
, HasOne()
, oder Set()
für das Ziel eines abhängigen Typs aufrufen, wird keine Ausnahme ausgelöst.
Warum?
Diese Änderung wurde vorgenommen, um eine deutlichere Trennung zwischen der Konfiguration des abhängigen Typs und der Beziehung zum abhängigen Typ zu ermöglichen.
Dadurch werden für Methoden wie HasForeignKey
Mehrdeutigkeiten und Unklarheiten beseitigt.
Gegenmaßnahmen
Passen Sie für die Beziehungen von abhängigen Typen die Konfiguration so an, dass die neue Fluent-API wie im obigen Beispiel verwendet wird.
Abhängige Entitäten, die die Tabelle gemeinsam mit dem Prinzipal verwenden, sind jetzt optional
Altes Verhalten
Sehen Sie sich das folgende -Modell an:
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public OrderDetails Details { get; set; }
}
public class OrderDetails
{
public int Id { get; set; }
public string ShippingAddress { get; set; }
}
Wenn OrderDetails
im Besitz von Order
ist oder explizit derselben Tabelle zugeordnet ist, war vor Version 3.0 in EF Core immer eine OrderDetails
-Instanz beim Hinzufügen eines neuen Order
-Objekts erforderlich.
Neues Verhalten
Ab Version 3.0 bietet EF Core die Möglichkeit, Order
ohne OrderDetails
hinzuzufügen, und alle OrderDetails
-Eigenschaften, mit Ausnahme des primären Schlüssels, werden Spalten zugeordnet, die NULL-Werte zulassen.
Bei Abfragen von EF Core wird OrderDetails
auf null
festgelegt, wenn eine der erforderlichen Eigenschaften keinen Wert aufweist oder keine erforderlichen Eigenschaften außer dem primären Schlüssel vorhanden und alle Eigenschaften null
sind.
Gegenmaßnahmen
Wenn Ihr Modell von der gemeinsamen Nutzung einer Tabelle mit allen optionalen Spalten abhängig ist, aber die Navigation, die darauf zeigt, wahrscheinlich nicht null
ist, sollte die Anwendung für die Handhabung von Fällen geändert werden, in denen die Navigation null
ist. Wenn das nicht möglich ist, sollte dem Entitätstyp eine erforderliche Eigenschaft hinzugefügt werden oder mindestens einer Eigenschaft sollte ein Wert ungleich null
zugewiesen sein.
Alle Entitäten, die eine Tabelle mit einer Spalte für das Parallelitätstoken gemeinsam verwenden, müssen diese einer Eigenschaft zuordnen
Altes Verhalten
Sehen Sie sich das folgende -Modell an:
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public byte[] Version { get; set; }
public OrderDetails Details { get; set; }
}
public class OrderDetails
{
public int Id { get; set; }
public string ShippingAddress { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.Property(o => o.Version).IsRowVersion().HasColumnName("Version");
}
Wenn OrderDetails
im Besitz von Order
ist oder explizit derselben Tabelle zugeordnet ist, wird vor Version 3.0 in EF Core durch das alleinige Aktualisieren von OrderDetails
der Version
-Wert auf dem Client nicht aktualisiert, und das nächste Update schlägt fehl.
Neues Verhalten
Ab Version 3.0 gibt EF Core den neuen Version
-Wert an Order
weiter, wenn dies Besitzer von OrderDetails
ist. Andernfalls wird eine Ausnahme während der Modellvalidierung ausgelöst.
Warum?
Diese Änderung wurde vorgenommen, um einen veralteten Wert für ein Parallelitätstoken zu vermeiden, wenn nur eine der Entitäten, die derselben Tabelle zugeordnet sind, aktualisiert wird.
Gegenmaßnahmen
Alle Entitäten, die die Tabelle gemeinsam nutzen, müssen eine Eigenschaft enthalten, die der Spalte für das Parallelitätstoken zugeordnet ist. Es ist möglich, eine solche im Schattenzustand zu erstellen:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<OrderDetails>()
.Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
}
Benutzereigene Entitäten können nicht abgefragt werden, ohne dass der Besitzer eine Nachverfolgungsabfrage verwendet
Altes Verhalten
Vor EF Core 3.0 konnten die benutzereigenen Entitäten wie jedes andere Navigationselement abgefragt werden.
context.People.Select(p => p.Address);
Neues Verhalten
Ab EF Core 3.0 wird eine Ausnahme ausgelöst, wenn eine Nachverfolgungsabfrage eine benutzereigene Entität ohne den Besitzer projiziert.
Warum?
Benutzereigene Entitäten können ohne den Besitzer nicht bearbeitet werden, daher handelt es sich in den meisten Fällen um einen Fehler, wenn sie auf diese Weise abgefragt werden.
Gegenmaßnahmen
Wenn die benutzereigene Entität nachverfolgt werden soll, damit sie später Änderungen an ihr vorgenommen werden können, sollte der Besitzer in der Abfrage enthalten sein.
Fügen Sie andernfalls einen AsNoTracking()
-Aufruf hinzu:
context.People.Select(p => p.Address).AsNoTracking();
Geerbte Eigenschaften von nicht zugeordneten Typen sind nun einer einzelnen Spalte für alle abgeleiteten Typen zugeordnet
Altes Verhalten
Sehen Sie sich das folgende -Modell an:
public abstract class EntityBase
{
public int Id { get; set; }
}
public abstract class OrderBase : EntityBase
{
public int ShippingAddress { get; set; }
}
public class BulkOrder : OrderBase
{
}
public class Order : OrderBase
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<OrderBase>();
modelBuilder.Entity<EntityBase>();
modelBuilder.Entity<BulkOrder>();
modelBuilder.Entity<Order>();
}
Vor Version 3.0 wurde in EF Core die Eigenschaft ShippingAddress
standardmäßig separaten Spalten für BulkOrder
und Order
zugeordnet.
Neues Verhalten
Ab Version 3.0 erstellt EF Core nur eine Spalte für ShippingAddress
.
Warum?
Das alte Verhalten war unerwartet.
Gegenmaßnahmen
Die Eigenschaft kann noch immer explizit einer separaten Spalte für die abgeleiteten Typen zugeordnet werden:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<OrderBase>();
modelBuilder.Entity<EntityBase>();
modelBuilder.Entity<BulkOrder>()
.Property(o => o.ShippingAddress).HasColumnName("BulkShippingAddress");
modelBuilder.Entity<Order>()
.Property(o => o.ShippingAddress).HasColumnName("ShippingAddress");
}
Die Konvention zur Fremdschlüsseleigenschaft entspricht nicht mehr dem Namen der Prinzipaleigenschaft
Altes Verhalten
Sehen Sie sich das folgende -Modell an:
public class Customer
{
public int CustomerId { get; set; }
public ICollection<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
}
Vor Version 3.0 wurde in EF Core gemäß der Konvention die CustomerId
-Eigenschaft für den Fremdschlüssel verwendet.
Wenn Order
jedoch ein abhängiger Typ ist, wäre CustomerId
der Primärschlüssel, was normalerweise nicht der Erwartungshaltung entspricht.
Neues Verhalten
Ab Version 3.0 werden in EF Core konventionsgemäß keine Eigenschaften mehr für Fremdschlüssel verwendet, wenn diese denselben Namen wie die Prinzipaleigenschaft besitzen. Die Muster für den Namen des Prinzipaltyps, der mit dem Namen der Prinzipaleigenschaft verkettet wird, und für den Navigationsnamen, der mit dem Namen der Prinzipaleigenschaft verkettet wird, werden jedoch weiterhin abgeglichen. Beispiel:
public class Customer
{
public int Id { get; set; }
public ICollection<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
}
public class Customer
{
public int Id { get; set; }
public ICollection<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int BuyerId { get; set; }
public Customer Buyer { get; set; }
}
Warum?
Diese Änderung wurde vorgenommen, um zu vermeiden, dass versehentlich eine Primärschlüsseleigenschaft für den abhängigen Typ definiert wird.
Gegenmaßnahmen
Wenn die Eigenschaft als Fremdschlüssel und daher Teil des Primärschlüssels vorgesehen war, müssen Sie diese explizit als solche festlegen.
Die Datenbankverbindung wird jetzt getrennt, wenn sie nicht mehr verwendet wird, bevor TransactionScope abgeschlossen wurde
Altes Verhalten
Wenn vor Version 3.0 in EF Core der Kontext die Verbindung in einem TransactionScope
öffnet, bleibt die Verbindung geöffnet, während der aktuelle TransactionScope
aktiv ist.
using (new TransactionScope())
{
using (AdventureWorks context = new AdventureWorks())
{
context.ProductCategories.Add(new ProductCategory());
context.SaveChanges();
// Old behavior: Connection is still open at this point
var categories = context.ProductCategories().ToList();
}
}
Neues Verhalten
Ab Version 3.0 schließt EF Core die Verbindung, sobald sie nicht mehr verwendet wird.
Warum?
Diese Änderung ermöglicht die Verwendung mehrerer Kontexte in demselben TransactionScope
. Das neue Verhalten entspricht außerdem EF6.
Gegenmaßnahmen
Wenn die Verbindung geöffnet bleiben muss, wird durch einen expliziten Aufruf von OpenConnection()
sichergestellt, dass EF Core diese nicht vorzeitig schließt:
using (new TransactionScope())
{
using (AdventureWorks context = new AdventureWorks())
{
context.Database.OpenConnection();
context.ProductCategories.Add(new ProductCategory());
context.SaveChanges();
var categories = context.ProductCategories().ToList();
context.Database.CloseConnection();
}
}
Für jede Eigenschaft wird separat ein ganzzahliger speicherinterner Schlüssel generiert
Altes Verhalten
Vor Version 3.0 wurde in EF Core ein gemeinsam verwendeter Wertegenerator für alle speicherinternen ganzzahligen Schlüsseleigenschaften verwendet.
Neues Verhalten
Ab Version 3.0 von EF Core wird für jede ganzzahlige Schlüsseleigenschaft ein eigener Wertegenerator bei der Verwendung der In-Memory Database verwendet. Wenn die Datenbank gelöscht wird, wird die Schlüsselgenerierung für alle Tabellen zurückgesetzt.
Warum?
Diese Änderung wurde vorgenommen, um die speicherinterne Schlüsselgenerierung enger mit der Schlüsselgenerierung für echte Datenbanken abzustimmen. Außerdem sollen bei Verwendung der In-Memory Database Tests leichter voneinander isoliert werden können.
Gegenmaßnahmen
Wenn Anwendungen von bestimmten speicherinternen Schlüsselwerten abhängig sind, funktionieren Erstere durch die eingeführte Änderung möglicherweise nicht mehr. Versuchen Sie, Abhängigkeiten von bestimmten Schlüsselwerten zu vermeiden, oder passen Sie die Anwendung an das neue Verhalten an.
Unterstützungsfelder werden standardmäßig verwendet
Altes Verhalten
Vor Version 3.0 wurde in EF Core mithilfe der Getter- und Settermethoden standardmäßig der Eigenschaftswert gelesen und geschrieben. Das war selbst dann der Fall, wenn das Unterstützungsfeld für eine Eigenschaft nicht bekannt war. Eine Ausnahme war die Abfrageausführung, bei der das Unterstützungsfeld direkt festgelegt wurde, wenn es bekannt war.
Neues Verhalten
Ab Version 3.0 wird in EF Core die Eigenschaft mithilfe des Unterstützungsfelds gelesen und geschrieben, wenn dieses für eine Eigenschaft bekannt ist. Dies kann dazu führen, dass eine Anwendung nicht mehr funktioniert, wenn sie auf zusätzliches Verhalten angewiesen ist, das im Code der Getter- und Settermethoden definiert ist.
Warum?
Diese Änderung wurde vorgenommen, um zu verhindern, dass in EF Core fälschlicherweise immer dann Geschäftslogik ausgelöst wird, wenn Datenbankvorgänge für Entitäten durchgeführt werden.
Gegenmaßnahmen
Sie können das Verhalten von vor Version 3.0 wiederherstellen, indem Sie in ModelBuilder
den Zugriffsmodus für die Eigenschaft konfigurieren.
Beispiel:
modelBuilder.UsePropertyAccessMode(PropertyAccessMode.PreferFieldDuringConstruction);
Eine Ausnahme wird ausgelöst, wenn mehrere kompatible Unterstützungsfelder gefunden werden
Altes Verhalten
Vor Version 3.0 wurde in EF Core ein Unterstützungsfeld auf Grundlage einer Rangfolge ausgewählt, wenn die Regeln zum Suchen eines Eigenschaftsfelds auf mehrere Felder zutrafen. Dabei konnte es vorkommen, dass in mehrdeutigen Fällen das falsche Feld verwendet wurde.
Neues Verhalten
Ab Version 3.0 wird in EF Core eine Ausnahme ausgelöst, wenn sich mehrere Felder auf dieselbe Eigenschaft beziehen.
Warum?
Diese Änderung wurde vorgenommen, um zu vermeiden, dass automatisch ein falsches Feld verwendet wird.
Gegenmaßnahmen
Für Eigenschaften mit mehrdeutigen Unterstützungsfeldern muss das zu verwendende Feld explizit festgelegt werden. Mit der Fluent-API ist dies beispielsweise wie folgt möglich:
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.HasField("_id");
„Nur-Feld“-Eigenschaftennamen sollten dem Feldnamen entsprechen
Altes Verhalten
Vor Version 3.0 konnte in EF Core eine Eigenschaft durch einen Zeichenfolgenwert angegeben werden, und wenn keine Eigenschaft mit diesem Namen im .NET-Typ gefunden wurde, versuchte EF Core, mithilfe von Konventionsregeln ein übereinstimmendes Feld zu finden.
private class Blog
{
private int _id;
public string Name { get; set; }
}
modelBuilder
.Entity<Blog>()
.Property("Id");
Neues Verhalten
Ab Version 3.0 muss in EF Core eine „Nur-Feld“-Eigenschaft mit dem Namen des Felds genau übereinstimmen.
modelBuilder
.Entity<Blog>()
.Property("_id");
Warum?
Diese Änderung wurde vorgenommen, um zu vermeiden, dass das gleiche Feld für zwei Eigenschaften mit ähnlichem Namen verwendet wird. Durch die Änderung entsprechen nun auch die Übereinstimmungsregeln für „Nur-Feld“-Eigenschaften den Regeln für Eigenschaften, die CLR-Eigenschaften zugeordnet sind.
Gegenmaßnahmen
„Nur-Feld“-Eigenschaften müssen so benannt werden wie das Feld, dem sie zugeordnet sind. In einer kommenden Version von EF Core nach 3.0 soll das explizite Konfigurieren eines Feldnamens, der sich vom Eigenschaftsnamen unterscheidet, erneut aktiviert werden (siehe Problem #15307):
modelBuilder
.Entity<Blog>()
.Property("Id")
.HasField("_id");
AddLogging und AddMemoryCache werden nicht mehr von AddDbContext/AddDbContextPool aufgerufen
Altes Verhalten
Vor EF Core 3.0 wurden beim Aufruf von AddDbContext
oder AddDbContextPool
durch Aufrufe von AddLogging und AddMemoryCache auch Protokollierungs- und Arbeitsspeichercachingdienste bei DI registriert.
Neues Verhalten
Ab EF Core 3.0 werden diese Dienste durch AddDbContext
und AddDbContextPool
nicht mehr bei Dependency Injection (DI) registriert.
Warum?
Für EF Core 3.0 ist es nicht erforderlich, dass diese Dienste im DI-Container der Anwendung enthalten sind. Wenn ILoggerFactory
jedoch im DI-Container der Anwendung registriert ist, wird sie nach wie vor von EF Core verwendet.
Gegenmaßnahmen
Wenn Ihre Anwendung diese Dienste benötigt, registrieren Sie sie explizit über AddLogging oder AddMemoryCache beim DI-Container.
AddEntityFramework* fügt IMemoryCache mit einer Größenbeschränkung hinzu
Altes Verhalten
Vor EF Core 3.0 wurden bei Aufrufen von AddEntityFramework*
-Methoden auch Arbeitsspeichercachingdienste ohne eine Größenbeschränkung bei DI registriert.
Neues Verhalten
Ab EF Core 3.0 registriert AddEntityFramework*
einen IMemoryCache-Dienst mit einer Größenbeschränkung. Wenn danach weitere Dienste hinzugefügt werden, die von IMemoryCache abhängig sind, können diese schnell den Standardgrenzwert erreichen, was zu Ausnahmen und Leistungsbeeinträchtigungen führt.
Warum?
Die Verwendung von IMemoryCache ohne Einschränkung kann zu unkontrollierter Arbeitsspeicherauslastung führen, wenn ein Fehler in die Abfragecachelogik auftritt oder wenn die Abfragen dynamisch generiert werden. Durch eine Standardeinschränkung können potenzielle DoS-Angriffe verhindert werden.
Gegenmaßnahmen
In den meisten Fällen ist das Aufrufen von AddEntityFramework*
nicht erforderlich, wenn AddDbContext
oder AddDbContextPool
ebenfalls aufgerufen werden. Daher besteht die beste Entschärfung darin, den AddEntityFramework*
-Aufruf zu entfernen.
Wenn Ihre Anwendung diese Dienste erfordert, registrieren Sie im Voraus eine IMemoryCache-Implementierung explizit mit dem DI-Container mithilfe von AddMemoryCache.
DbContext.Entry erkennt nun mit DetectChanges lokale Änderungen
Altes Verhalten
Vor Version 3.0 führte in EF Core ein Aufruf von DbContext.Entry
dazu, dass Änderungen an allen überwachten Entitäten erkannt wurden.
Dadurch wurde sichergestellt, dass der Zustand in EntityEntry
immer aktuell war.
Neues Verhalten
Ab Version 3.0 werden in EF Core Änderungen bei einem Aufruf von DbContext.Entry
nur in der angegebenen Entität und in allen überwachten Prinzipalentitäten erkannt.
Änderungen an anderer Stelle werden daher durch den Aufruf dieser Methode möglicherweise nicht erkannt, was sich auf den Anwendungszustand auswirken kann.
Beachten Sie, dass selbst die Erkennung lokaler Änderungen deaktiviert wird, wenn ChangeTracker.AutoDetectChangesEnabled
auf false
festgelegt wird.
Andere Methoden wie ChangeTracker.Entries
und SaveChanges
, die eine Änderungserkennung auslösen, führen nach wie vor zu einem vollständigen DetectChanges
-Vorgang für alle überwachten Entitäten.
Warum?
Diese Änderung wurde vorgenommen, um die Leistung bei der standardmäßigen Verwendung von context.Entry
zu verbessern.
Gegenmaßnahmen
Rufen Sie ChangeTracker.DetectChanges()
explizit vor dem Aufruf von Entry
auf, wenn das Verhalten vor Version 3.0 beibehalten werden soll.
Schlüssel aus Zeichenfolgen und Bytearrays werden standardmäßig nicht vom Client generiert
Altes Verhalten
Vor Version 3.0 konnten in EF Core string
- und byte[]
-Schlüsseleigenschaften verwendet werden, ohne dass explizit ein anderer Wert als NULL festgelegt werden musste.
In diesem Fall wurde der Schlüsselwert auf dem Client als GUID generiert und für byte[]
in Bytes serialisiert.
Neues Verhalten
Ab Version 3.0 wird in EF Core eine Ausnahme ausgelöst, wenn kein Schlüsselwert festgelegt wird.
Warum?
Diese Änderung wurde vorgenommen, da vom Client generierte string
/byte[]
-Werte in der Regel nicht sinnvoll sind. Das Standardverhalten führte außerdem zu kaum nachvollziehbaren generierten Schlüsselwerten.
Gegenmaßnahmen
Wenn Sie das Verhalten vor Version 3.0 nutzen möchten, müssen Sie explizit festlegen, dass für die Schlüsseleigenschaften generierte Werte verwendet sollen, wenn kein anderer Nicht-NULL-Wert festgelegt wird. Dies ist beispielsweise mit der Fluent-API möglich:
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.ValueGeneratedOnAdd();
Alternativ bieten sich auch Datenanmerkungen an:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
ILoggerFactory ist nun ein bereichsbezogener Dienst
Altes Verhalten
Vor Version 3.0 wurde ILoggerFactory
in EF Core als Singletondienst registriert.
Neues Verhalten
Ab Version 3.0 wird in EF Core ILoggerFactory
als bereichsbezogener Dienst registriert.
Warum?
Diese Änderung wurde vorgenommen, um einer DbContext
-Instanz eine Protokollierung zuordnen zu können. Dadurch werden weitere Funktionen bereitgestellt. In einigen Fällen wird außerdem schädliches Verhalten wie etwa die schnelle Zunahme von internen Dienstanbietern beseitigt.
Gegenmaßnahmen
Diese Änderung wirkt sich nur dann auf den Anwendungscode aus, wenn benutzerdefinierte Dienste auf dem internen Dienstanbieter von EF Core registriert und verwendet werden.
Ein solches Szenario ist aber unüblich.
In einem derartigen Fall funktioniert fast alles wie vorher. Singletondienste, die von ILoggerFactory
abhängig sind, müssen jedoch so angepasst werden, dass ILoggerFactory
auf andere Weise abgerufen wird.
Erstellen Sie in diesen Situationen bitte im GitHub-Issuetracker für EF Core ein Issue, in dem Sie beschreiben, wie Sie ILoggerFactory
verwenden. So können wir in Zukunft erneute Breaking Changes vermeiden.
Für Proxys mit Lazy Loading (trägem Laden) gilt nicht mehr die Annahme, dass Navigationseigenschaften vollständig geladen sind
Altes Verhalten
Vor Version 3.0 gab es in EF Core nach der Freigabe eines DbContext
-Objekts keine Möglichkeit, zu ermitteln, ob eine aus dem Kontext abgerufene Navigationseigenschaft für eine Entität vollständig geladen war.
Stattdessen galt für Proxys die Annahme, dass eine Verweisnavigation geladen ist, falls für diese ein anderer Wert als NULL vorliegt. Außerdem wurde davon ausgegangen, dass eine Sammlungsnavigation geladen ist, wenn sie nicht leer ist.
In diesen Fällen entsprach das Lazy Loading einer Nulloperation („No-Op“).
Neues Verhalten
Ab Version 3.0 überwachen in EF Core Proxys, ob eine Navigationseigenschaft geladen ist. Wenn also auf eine Navigationseigenschaft zugegriffen werden soll, die nach der Freigabe des Kontexts geladen wird, ist dies immer eine No-Op-Operation. Das ist selbst dann der Fall, wenn die geladene Navigation leer oder NULL ist. Wenn umgekehrt auf eine nicht geladene Navigationseigenschaft zugegriffen werden soll und der Kontext bereits freigegeben wurde, wird eine Ausnahme ausgelöst. Dieser Fall tritt auch dann ein, wenn die Navigationseigenschaft eine nicht leere Sammlung ist. In dieser Situation wird also vom Anwendungscode versucht, zu einem ungültigen Zeitpunkt Lazy Loading durchzuführen. Die Anwendung sollte so angepasst werden, dass dieser Vorgang vermieden wird.
Warum?
Diese Änderung wurde vorgenommen, um das Verhalten beim Lazy Loading einer freigegebenen DbContext
-Instanz konsistent und korrekt zu gestalten.
Gegenmaßnahmen
Aktualisieren Sie den Anwendungscode so, dass kein Lazy Loading durchgeführt wird, nachdem der Kontext freigegeben wurde. Alternativ können Sie auch, wie in der Ausnahmemeldung beschrieben wird, eine No-Op-Operation festlegen.
Die Erstellung zu vieler interner Dienstanbieter führt standardmäßig zu einem Fehler
Altes Verhalten
Vor Version 3.0 wurde in EF Core eine Warnung für eine Anwendung protokolliert, die unverhältnismäßig viele interne Dienstanbieter erstellte.
Neues Verhalten
Ab Version 3.0 wird diese Warnung in EF Core als Fehler betrachtet, und eine Ausnahme wird ausgelöst.
Warum?
Diese Änderung wurde vorgenommen, damit auf den oben beschriebenen Fall explizit hingewiesen wird. So soll die Entwicklung von besserem Anwendungscode erleichtert werden.
Gegenmaßnahmen
Wenn dieser Fehler auftritt, müssen Sie die Grundursache ermitteln und die Erstellung vieler interner Dienstanbieter unterbinden.
Sie können den Fehler allerdings auch wieder in eine Warnung konvertierten (oder ihn ignorieren), indem Sie DbContextOptionsBuilder
konfigurieren.
Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.ConfigureWarnings(w => w.Log(CoreEventId.ManyServiceProvidersCreatedWarning));
}
Neues Verhalten für HasOne/HasMany bei Aufruf mit einer einzelnen Zeichenfolge
Altes Verhalten
Vor EF Core 3.0 wurde Code, der HasOne
oder HasMany
mit einer einzelnen Zeichenfolge aufruft, auf irritierende Weise interpretiert.
Beispiel:
modelBuilder.Entity<Samurai>().HasOne("Entrance").WithOne();
Der Code scheint Samurai
mit einem anderen Entitätstyp über die Entrance
-Navigationseigenschaft zu verknüpfen, die möglicherweise privat ist.
Tatsächlich versucht dieser Code, eine Beziehung zu einem Entitätstyp namens Entrance
ohne Navigationseigenschaft herzustellen.
Neues Verhalten
Ab EF Core 3.0 führt der obige Code jetzt die erwartete Aktion durch.
Warum?
Das alte Verhalten war sehr verwirrend, vor allem beim Lesen des Konfigurationscodes und bei der Suche nach Fehlern.
Gegenmaßnahmen
Dies führt nur zu Fehlern in Anwendungen, die Beziehungen explizit unter Verwendung von Zeichenfolgen für Typnamen und ohne explizite Angabe der Navigationseigenschaft konfigurieren.
Dies ist nicht üblich.
Das vorherige Verhalten kann durch explizite Übergabe von null
für den Namen der Navigationseigenschaft erzielt werden.
Beispiel:
modelBuilder.Entity<Samurai>().HasOne("Some.Entity.Type.Name", null).WithOne();
Der Rückgabetyp für mehrere asynchrone Methoden wurde von Task in ValueTask geändert
Altes Verhalten
Die folgenden asynchronen Methoden gaben zuvor Task<T>
zurück:
DbContext.FindAsync()
DbSet.FindAsync()
DbContext.AddAsync()
DbSet.AddAsync()
ValueGenerator.NextValueAsync()
(und abgeleitete Klassen)
Neues Verhalten
Die oben genannten Methoden geben nun ValueTask<T>
über dieselbe T
wie zuvor zurück.
Warum?
Durch diese Änderung verringert sich die Anzahl der Heapzuordnungen, die beim Aufrufen dieser Methoden entstehen, und dies führt zu einer Verbesserung der allgemeinen Leistung.
Gegenmaßnahmen
Anwendungen, die einfach die oben genannten APIs erwarten, müssen nur neu kompiliert werden – Quelländerungen sind nicht erforderlich.
Eine komplexere Verwendung (z.B. Übergeben der zurückgegebenen Task
an Task.WhenAny()
) erfordern in der Regel, dass die zurückgegebene ValueTask<T>
durch einen Aufruf von AsTask()
in Task<T>
konvertiert wird.
Beachten Sie, dass dadurch die mit dieser Änderung verbundene Verringerung der Zuordnungen aufgehoben wird.
Die Anmerkung Relational:TypeMapping heißt nun nur noch TypeMapping
Altes Verhalten
Der Anmerkungsname für Typzuordnungsanmerkungen war bisher Relational:TypeMapping.
Neues Verhalten
Der Anmerkungsname für Typzuordnungsanmerkungen lautet nun TypeMapping.
Warum?
Typzuordnungen werden nicht mehr ausschließlich für Anbieter relationaler Datenbanken verwendet.
Gegenmaßnahmen
Diese Änderung ist nur dann ein Breaking Change, wenn Anwendungen direkt über eine Anmerkung auf die Typzuordnung zugreifen. Dieses Szenario ist jedoch unüblich. Verzichten Sie daher darauf, die Anmerkung direkt zu verwenden, und greifen Sie stattdessen über die Fluent-API auf Typzuordnungen zu, um das Problem zu beheben.
ToTable löst für einen abgeleiteten Typ eine Ausnahme aus
Altes Verhalten
Vor Version 3.0 wurde in EF Core der Aufruf von ToTable()
für einen abgeleiteten Typ ignoriert, da TPH die einzige Strategie zur Vererbungszuordnung war und diese in unzulässigen Fällen angewendet wurde.
Neues Verhalten
Ab Version 3.0 wird in EF Core eine Ausnahme ausgelöst, um später eine unerwartete Zuordnung zu vermeiden, wenn ToTable()
für einen abgeleiteten Typ aufgerufen wird. Dieser Schritt dient auch als Vorbereitung für die TPT- und TPC-Unterstützung, die in einem künftigen Release folgen wird.
Warum?
Derzeit ist es nicht zulässig, einen abgeleiteten Typ einer anderen Tabelle zuzuordnen. Durch diese Änderung werden Breaking Changes vermieden, wenn der beschriebene Vorgang in Zukunft zulässig wird.
Gegenmaßnahmen
Entfernen Sie alle Zuordnungen von abgeleiteten Typen zu anderen Tabellen.
ForSqlServerHasIndex wurde durch HasIndex ersetzt
Altes Verhalten
Vor Version 3.0 konnten in EF Core mit ForSqlServerHasIndex().ForSqlServerInclude()
Spalten konfiguriert werden, die mit INCLUDE
verwendet wurden.
Neues Verhalten
Ab Version 3.0 wird in EF Core die Nutzung von Include
für einen Index auf der relationalen Ebene unterstützt.
Verwenden Sie HasIndex().ForSqlServerInclude()
.
Warum?
Diese Änderung wurde vorgenommen, um die API für Indizes mit Include
an einer zentralen Stelle für alle Datenbankanbieter zusammenzuführen.
Gegenmaßnahmen
Verwenden Sie wie oben beschrieben die neue API.
Metadaten-API-Änderungen
Neues Verhalten
Die folgenden Eigenschaften wurden in Erweiterungsmethoden konvertiert:
IEntityType.QueryFilter
->GetQueryFilter()
IEntityType.DefiningQuery
->GetDefiningQuery()
IProperty.IsShadowProperty
->IsShadowProperty()
IProperty.BeforeSaveBehavior
->GetBeforeSaveBehavior()
IProperty.AfterSaveBehavior
->GetAfterSaveBehavior()
Warum?
Mit dieser Änderung wird die Implementierung der oben genannten Schnittstellen vereinfacht.
Gegenmaßnahmen
Verwenden Sie die neuen Erweiterungsmethoden.
Anbieterspezifische Metadaten-API-Änderungen
Neues Verhalten
Die anbieterspezifischen Erweiterungsmethoden werden vereinfacht:
IProperty.Relational().ColumnName
->IProperty.GetColumnName()
IEntityType.SqlServer().IsMemoryOptimized
->IEntityType.IsMemoryOptimized()
PropertyBuilder.UseSqlServerIdentityColumn()
->PropertyBuilder.UseIdentityColumn()
Warum?
Mit dieser Änderung wird die Implementierung der oben genannten Erweiterungsmethoden vereinfacht.
Gegenmaßnahmen
Verwenden Sie die neuen Erweiterungsmethoden.
EF Core sendet keine PRAGMA-Anweisungen mehr, um Fremdschlüssel in SQLite zu erzwingen
Altes Verhalten
Vor Version 3.0 wurden in EF Core PRAGMA foreign_keys = 1
-Anweisungen gesendet, wenn eine Verbindung mit SQLite hergestellt wurde.
Neues Verhalten
Ab Version 3.0 werden in EF Core keine PRAGMA foreign_keys = 1
-Anweisungen mehr gesendet, wenn eine Verbindung mit SQLite hergestellt wird.
Warum?
Diese Änderung wurde vorgenommen, da von EF Core standardmäßig SQLitePCLRaw.bundle_e_sqlite3
verwendet wird. Dadurch ist die Erzwingung von Fremdschlüsseln standardmäßig aktiviert und muss nicht jedes Mal explizit aktiviert werden, wenn eine Verbindung hergestellt wird.
Gegenmaßnahmen
Fremdschlüssel sind in SQLitePCLRaw.bundle_e_sqlite3 standardmäßig aktiviert. Dieses Bundle wird standardmäßig von EF Core verwendet.
In anderen Fällen können Fremdschlüssel durch die Angabe Foreign Keys=True
in der Verbindungszeichenfolge aktiviert werden.
SQLitePCLRaw.bundle_e_sqlite3 ist nun eine Abhängigkeit von Microsoft.EntityFrameworkCore.Sqlite
Altes Verhalten
Vor Version 3.0 wurde von EF Core SQLitePCLRaw.bundle_green
verwendet.
Neues Verhalten
Ab Version 3.0 wird von EF Core SQLitePCLRaw.bundle_e_sqlite3
verwendet.
Warum?
Diese Änderung wurde vorgenommen, damit die unter iOS verwendete Version von SQLite sich konsistent zu Versionen auf anderen Plattformen verhält.
Gegenmaßnahmen
Konfigurieren Sie Microsoft.Data.Sqlite
so, dass ein anderes SQLitePCLRaw
-Bundle verwendet wird, um die native SQLite-Version unter iOS zu nutzen.
GUID-Werte werden jetzt als TEXT in SQLite gespeichert
Altes Verhalten
GUID-Werte wurden in der Vergangenheit in SQLite als BLOB-Werte gespeichert.
Neues Verhalten
GUID-Werte werden jetzt als TEXT gespeichert.
Warum?
Das Binärformat der GUIDs ist nicht standardisiert. Das Speichern der Werte als TEXT führt dazu, dass die Datenbank mit anderen Technologien eher kompatibel ist.
Gegenmaßnahmen
Sie können vorhandene Datenbanken zum neuen Format migrieren, indem Sie SQL-Code wie den folgenden ausführen:
UPDATE MyTable
SET GuidColumn = hex(substr(GuidColumn, 4, 1)) ||
hex(substr(GuidColumn, 3, 1)) ||
hex(substr(GuidColumn, 2, 1)) ||
hex(substr(GuidColumn, 1, 1)) || '-' ||
hex(substr(GuidColumn, 6, 1)) ||
hex(substr(GuidColumn, 5, 1)) || '-' ||
hex(substr(GuidColumn, 8, 1)) ||
hex(substr(GuidColumn, 7, 1)) || '-' ||
hex(substr(GuidColumn, 9, 2)) || '-' ||
hex(substr(GuidColumn, 11, 6))
WHERE typeof(GuidColumn) == 'blob';
Sie können in EF Core auch weiterhin das alte Verhalten verwenden, indem Sie einen Wertkonverter für diese Eigenschaften konfigurieren.
modelBuilder
.Entity<MyEntity>()
.Property(e => e.GuidProperty)
.HasConversion(
g => g.ToByteArray(),
b => new Guid(b));
Microsoft.Data.Sqlite kann weiterhin GUID-Werte aus BLOB- und TEXT-Spalten lesen. Da sich jedoch das Standardformat für Parameter und Konstanten geändert hat, müssen Sie wahrscheinlich Aktionen für die meisten GUID-Szenarios vornehmen.
Char-Werte werden jetzt als TEXT in SQLite gespeichert
Altes Verhalten
Char-Werte wurden in der Vergangenheit in SQLite als INTEGER-Werte gespeichert. Der Char-Wert A wurde z.B. als INTEGER-Wert 65 gespeichert.
Neues Verhalten
Char-Werte werden jetzt als TEXT gespeichert.
Warum?
Das Speichern der Werte als TEXT ist naheliegender und führt dazu, dass die Datenbank mit anderen Technologien eher kompatibel ist.
Gegenmaßnahmen
Sie können vorhandene Datenbanken zum neuen Format migrieren, indem Sie SQL-Code wie den folgenden ausführen:
UPDATE MyTable
SET CharColumn = char(CharColumn)
WHERE typeof(CharColumn) = 'integer';
Sie können in EF Core auch weiterhin das alte Verhalten verwenden, indem Sie einen Wertkonverter für diese Eigenschaften konfigurieren.
modelBuilder
.Entity<MyEntity>()
.Property(e => e.CharProperty)
.HasConversion(
c => (long)c,
i => (char)i);
Microsoft.Data.Sqlite kann auch weiterhin Zeichenwerte aus INTEGER- und TEXT-Spalten lesen, weshalb es sein kann, dass in einigen Szenarios keine weiteren Maßnahmen erforderlich sind.
Migrations-IDs werden jetzt mit dem Kalender der invarianten Kultur generiert
Altes Verhalten
Migrations-IDs wurden versehentlich mit dem Kalender der aktuellen Kultur generiert.
Neues Verhalten
Migrations-IDs werden jetzt immer mit dem Kalender der invarianten Kultur generiert (gregorianischer Kalender).
Warum?
Die Reihenfolge der Migrationen ist beim Aktualisieren einer Datenbank oder Auflösen von Mergekonflikten wesentlich. Wenn der invariante Kalender verwendet wird, werden Probleme bei der Reihenfolge vermieden, die entstehen, wenn Teammitglieder unterschiedliche Systemkalender verwenden.
Gegenmaßnahmen
Diese Änderung betrifft jeden Benutzer, der einen Kalender verwendet, der nicht gregorianisch ist und bei dem die Jahreszahl höher als die des gregorianischen Kalenders ist (wie z.B. in der buddhistischen Zeitrechnung). Vorhandene Migrations-IDs müssen aktualisiert werden, damit neue Migrationen in der Reihenfolge hinter vorhandenen Migrationen eingeordnet werden.
Sie können die Migrations-ID im Migration-Attribut in der Designerdatei der Migration finden.
[DbContext(typeof(MyDbContext))]
-[Migration("25620318122820_MyMigration")]
+[Migration("20190318122820_MyMigration")]
partial class MyMigration
{
Die Tabelle mit dem Migrationsverlauf muss auch aktualisiert werden.
UPDATE __EFMigrationsHistory
SET MigrationId = CONCAT(LEFT(MigrationId, 4) - 543, SUBSTRING(MigrationId, 4, 150))
UseRowNumberForPaging wurde entfernt
Altes Verhalten
Vor EF Core 3.0 konnte UseRowNumberForPaging
zum Generieren von SQL-Code für die Paginierung verwendet werden, der mit SQL Server 2008 kompatibel ist.
Neues Verhalten
Ab EF Core 3.0 generiert EF nur noch SQL-Code für die Paginierung, die mit höheren SQL Server-Versionen kompatibel ist.
Warum?
Wir führen diese Änderung ein, weil SQL Server 2008 kein unterstütztes Produkt mehr ist und weil die Aktualisierung dieses Features auf die in EF Core 3.0 vorgenommenen Änderungen an Abfragen ein wichtiger Schritt ist.
Gegenmaßnahmen
Es empfiehlt sich, eine Update auf eine neuere Version von SQL Server durchzuführen oder einen höheren Kompatibilitätsgrad zu verwenden, damit der generierte SQL-Code unterstützt wird. Wenn dies bei Ihnen nicht möglich ist, fügen Sie einen Kommentar zum Issue ein, und geben Sie alle relevanten Informationen an. Je nach Feedback der Benutzer wird diese Entscheidung möglicherweise noch einmal überprüft.
Erweiterungsinformationen und -metadaten wurden aus IDbContextOptionsExtension entfernt
Altes Verhalten
IDbContextOptionsExtension
enthielt Methoden zum Bereitstellen von Metadaten zur Erweiterung.
Neues Verhalten
Diese Methoden wurden in eine neue abstrakte DbContextOptionsExtensionInfo
-Basisklasse verschoben, die von einer neuen IDbContextOptionsExtension.Info
-Eigenschaft zurückgegeben werden.
Warum?
In den Releases zwischen 2.0 und 3.0 mussten wir diese Methoden mehrmals ergänzen oder ändern. Durch die Bereitstellung dieser Methoden in einer neuen abstrakten Basisklasse lassen sich solche Änderungen einfacher einführen, ohne dass es zu Konflikten mit vorhandenen Erweiterungen kommt.
Gegenmaßnahmen
Erweiterungen wurden aktualisiert und verwenden das neue Muster.
Beispiele finden sich in den vielen Implementierungen von IDbContextOptionsExtension
für verschiedene Arten von Erweiterungen im EF Core-Quellcode.
LogQueryPossibleExceptionWithAggregateOperator wurde umbenannt
Change
RelationalEventId.LogQueryPossibleExceptionWithAggregateOperator
wurde umbenannt in RelationalEventId.LogQueryPossibleExceptionWithAggregateOperatorWarning
.
Warum?
Die Benennung dieses Warnereignisses wurde an allen anderen Warnereignissen ausgerichtet.
Gegenmaßnahmen
Verwenden Sie den neuen Namen. (Beachten Sie, dass sich die Ereignis-ID-Nummer nicht geändert hat.)
Übersichtlichere API für Namen von Fremdschlüsselconstraints
Altes Verhalten
Vor EF Core 3.0 wurden Einschränkungsnamen von Fremdschlüsseln einfach als „Namen“ bezeichnet. Beispiel:
var constraintName = myForeignKey.Name;
Neues Verhalten
Ab EF Core 3.0 werden Einschränkungsnamen von Fremdschlüsseln nun als „Einschränkungsnamen“ bezeichnet. Beispiel:
var constraintName = myForeignKey.ConstraintName;
Warum?
Durch diese Änderung wird Konsistenz für Benennungen in diesem Bereich gewährleistet, und es wird verdeutlicht, dass es sich dabei um den Namen der Fremdschlüsseleinschränkung handelt und nicht um den Spalten- oder Eigenschaftennamen, auf deren Grundlage der Fremdschlüssel definiert ist.
Gegenmaßnahmen
Verwenden Sie den neuen Namen.
IRelationalDatabaseCreator.HasTables/HasTablesAsync ist jetzt öffentlich
Altes Verhalten
Vor EF Core 3.0 waren diese Methoden geschützt.
Neues Verhalten
Ab EF Core 3.0 sind diese Methoden öffentlich.
Warum?
Diese Methoden werden von EF verwendet, um zu ermitteln, ob eine Datenbank erstellt wurde, aber leer ist. Dies kann auch außerhalb von EF nützlich sein, um zu ermitteln, ob Migrationen angewendet werden sollen oder nicht.
Gegenmaßnahmen
Der Zugriff auf Überschreibungen wurde geändert.
Microsoft.EntityFrameworkCore.Design ist jetzt ein DevelopmentDependency-Paket
Altes Verhalten
Vor EF Core 3.0 war Microsoft.EntityFrameworkCore.Design ein reguläres NuGet-Paket, auf dessen Assembly von Projekten verwiesen werden kann, die davon abhängig sind.
Neues Verhalten
Ab EF Core 3.0 ist es ein DevelopmentDependency-Paket. Das bedeutet, dass die Abhängigkeit nicht transitiv auf andere Projekte übertragen wird und Sie nicht mehr standardmäßig auf die Assembly verweisen können.
Warum?
Dieses Paket ist nur für die Verwendung zur Entwurfszeit konzipiert. Bereitgestellte Anwendungen sollten nicht darauf verweisen. Die Kennzeichnung des Pakets als DevelopmentDependency unterstreicht diese Empfehlung.
Gegenmaßnahmen
Wenn Sie auf dieses Paket verweisen müssen, um das Verhalten von EF Core zur Entwurfszeit zu überschreiben, können Sie die Metadaten des PackageReference-Elements in Ihrem Projekt aktualisieren.
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<!-- Remove IncludeAssets to allow compiling against the assembly -->
<!--<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
</PackageReference>
Wenn transitiv über via Microsoft.EntityFrameworkCore.Tools auf das Paket verwiesen wird, müssen Sie dem Paket eine explizite PackageReference hinzufügen, um seine Metadaten zu ändern. Ein solch expliziter Verweis muss jedem Projekt hinzugefügt werden, für das die Typen aus den Paketen benötigt werden.
SQLitePCL.raw wurde auf Version 2.0.0 aktualisiert
Altes Verhalten
Microsoft.EntityFrameworkCore.Sqlite war früher von Version 1.1.12 von SQLitePCL.raw abhängig.
Neues Verhalten
Wir haben unser Paket aktualisiert, so dass es jetzt von Version 2.0.0 abhängig ist.
Warum?
Version 2.0.0 von SQLitePCL.raw zielt auf .NET Standard 2.0 ab. Zuvor war .NET Standard 1.1 das Ziel, hierfür war für ein umfassender Funktionsabschluss von transitiven Paketen erforderlich.
Gegenmaßnahmen
SQLitePCL.raw, Version 2.0.0, enthält einige Breaking Changes. Weitere Informationen finden Sie in den Versionshinweisen.
NetTopologySuite wurde auf Version 2.0.0 aktualisiert
Altes Verhalten
Die Pakete mit räumlichen Daten waren bisher von Version 1.15.1 der NetTopologySuite abhängig.
Neues Verhalten
Wir haben unser Paket aktualisiert, so dass es jetzt von Version 2.0.0 abhängig ist.
Warum?
Version 2.0.0 der NetTopologySuite behebt verschiedene Verwendungsprobleme, die von EF Core-Benutzern gemeldet wurden.
Gegenmaßnahmen
NetTopologySuite 2.0.0 umfasst einige Breaking Changes. Weitere Informationen finden Sie in den Versionshinweisen.
Microsoft.Data.SqlClient wird statt System.Data.SqlClient verwendet
Altes Verhalten
Microsoft.EntityFrameworkCore.SqlServer war zuvor von System.Data.SqlClient abhängig.
Neues Verhalten
Das Paket wurde so aktualisiert, dass es jetzt von Microsoft.Data.SqlClient abhängig ist.
Warum?
Microsoft.Data.SqlClient ist zukünftig der Haupttreiber für den Datenzugriff für SQL Server, und System.Data.SqlClient wird nicht länger der Fokus bei der Entwicklung sein. Einige wichtige Features wie Always Encrypted sind nur in Microsoft.Data.SqlClient verfügbar.
Gegenmaßnahmen
Wenn Ihr Code eine direkte Abhängigkeit von System.Data.SqlClient hat, müssen Sie diesen ändern, damit er stattdessen auf Microsoft.Data.SqlClient verweist. Da die beiden Pakete einen hohen Grad an API-Kompatibilität vorweisen, sollte dafür nur eine einfache Paket- und Namespaceänderung erforderlich sein.
Beziehungen mit mehreren mehrdeutigen Selbstverweisen müssen nun konfiguriert werden
Altes Verhalten
Ein Entitätstyp, der über unidirektionale Navigationseigenschaften mit mehreren Selbstverweisen und über übereinstimmende Fremdschlüssel verfügte, wurde bislang fälschlicherweise als einfache Beziehung konfiguriert. Beispiel:
public class User
{
public Guid Id { get; set; }
public User CreatedBy { get; set; }
public User UpdatedBy { get; set; }
public Guid CreatedById { get; set; }
public Guid? UpdatedById { get; set; }
}
Neues Verhalten
Dieses Szenario wird nun beim Erstellen von Modellen erkannt. Außerdem wird eine Ausnahme ausgelöst, in der darauf hingewiesen wird, dass das Modell mehrdeutig ist.
Warum?
Das resultierende Modell war mehrdeutig und führte in diesem Fall üblicherweise zu falschen Ergebnissen.
Gegenmaßnahmen
Konfigurieren Sie die Beziehung vollständig. Beispiel:
modelBuilder
.Entity<User>()
.HasOne(e => e.CreatedBy)
.WithMany();
modelBuilder
.Entity<User>()
.HasOne(e => e.UpdatedBy)
.WithMany();
DbFunction.Schema ist NULL oder eine leere Zeichenfolge und ist deshalb so konfiguriert, dass es im Standardschema des Modells ist
Altes Verhalten
Eine mit dem Schema als leere Zeichenfolge konfigurierte DbFunction wurde als integrierte Funktion ohne Schema behandelt. Der folgende Code ordnet z. B. die CLR-Funktion DatePart
der integrierten Funktion DATEPART
auf SqlServer zu.
[DbFunction("DATEPART", Schema = "")]
public static int? DatePart(string datePartArg, DateTime? date) => throw new Exception();
Neues Verhalten
Alle DbFunction-Zuordnungen werden als einer benutzerdefinierten Funktion zugeordnet betrachtet. Daher würde die Funktion durch einen leeren Zeichenfolgenwert in das Standardschema für das Modell eingefügt werden. Dies könnte das Schema sein, das explizit über eine fließende modelBuilder.HasDefaultSchema()
- oder anderweitig über eine dbo
-API konfiguriert wurde.
Warum?
Weil das Schema vorher leer war, konnte man erreichen, dass die Funktion integriert, aber die Logik nur für SqlServer anwendbar war, wo integrierte Funktionen nicht zu einem Schema gehören.
Gegenmaßnahmen
Konfigurieren Sie die Übersetzung von DbFunction manuell, um Sie einer integrierten Funktion zuzuordnen.
modelBuilder
.HasDbFunction(typeof(MyContext).GetMethod(nameof(MyContext.DatePart)))
.HasTranslation(args => SqlFunctionExpression.Create("DatePart", args, typeof(int?), null));
EF Core 3.0 zielt auf .NET Standard 2.1 und nicht auf .NET Standard 2.0 ab Zurückgesetzt
EF Core 3.0 zielt auf .NET Standard 2.1 ab, was ein Breaking Change ist, der .NET Framework-Anwendungen ausschließt. EF Core 3.1 hat dies zurückgesetzt und zielt wieder auf .NET Standard 2.0 ab.
Die Abfrageausführung wird auf Debugebene protokolliert – zurückgesetzt
Wir haben diese Änderung zurückgesetzt, weil die neue Konfiguration in EF Core 3.0 die Angabe des Protokolliergrads für jedes Ereignis durch die Anwendung ermöglicht. Um z.B. die Protokollierung von SQL zu Debug
zu ändern, konfigurieren Sie den Grad explizit in OnConfiguring
oder AddDbContext
:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(connectionString)
.ConfigureWarnings(c => c.Log((RelationalEventId.CommandExecuting, LogLevel.Debug)));