Ich führe einen Korrekturcode aus, der über einen großen Stapel von Entitäten läuft, während seine Geschwindigkeit abnimmt. Dies liegt daran, dass die Anzahl der verfolgten Entitäten im Kontext mit jeder Iteration zunimmt. Es kann lange dauern, sodass ich am Ende Änderungen speichere jeder Iteration. Jede Iteration ist unabhängig und ändert die zuvor geladenen Entitäten nicht.
Ich weiß, dass ich die Änderungsverfolgung deaktivieren kann, möchte dies aber nicht, da es sich nicht um einen Masseneinfügecode handelt, sondern um das Laden der Entitäten und das Berechnen einiger Dinge. Wenn die Zahlen nicht korrekt sind, setzen Sie die neuen Zahlen und aktualisieren / löschen / erstellen einige zusätzliche Entitäten. Ich weiß, dass ich für jede Iteration einen neuen DbContext erstellen kann, und das würde wahrscheinlich schneller laufen als alle in derselben Instanz, aber ich denke, dass es einen besseren Weg geben könnte.
Die Frage ist also: Gibt es eine Möglichkeit, die zuvor im Datenbankkontext geladenen Entitäten zu löschen?
quelle
context.Entry(entity).State = EntityState.Detached
und die Verfolgung dieser bestimmten Entität wird beendet.Antworten:
Sie können Ihrer
DbContext
oder einer Erweiterungsmethode eine Methode hinzufügen , die den ChangeTracker verwendet, um alle hinzugefügten, geänderten und gelöschten Entitäten zu trennen:public void DetachAllEntities() { var changedEntriesCopy = this.ChangeTracker.Entries() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified || e.State == EntityState.Deleted) .ToList(); foreach (var entry in changedEntriesCopy) entry.State = EntityState.Detached; }
quelle
var entity
sollte auch wirklich sein,var entry
da es der Eintrag ist, nicht die tatsächliche Entität)1. Möglichkeit: Trennen Sie den Eintrag
Wenn Sie den Eintrag trennen, beendet der Änderungs-Tracker die Verfolgung (und sollte zu einer besseren Leistung führen).
Siehe: http://msdn.microsoft.com/de-de/library/system.data.entitystate(v=vs.110).aspx
2. Möglichkeit: Arbeiten Sie mit Ihrem eigenen
Status
Feld + getrennten KontextenMöglicherweise möchten Sie den Status Ihrer Entität unabhängig steuern, damit Sie nicht verbundene Diagramme verwenden können. Fügen Sie eine Eigenschaft für den Entitätsstatus hinzu und wandeln Sie diesen Status in den Status um,
dbContext.Entry(entity).State
wenn Sie Vorgänge ausführen (verwenden Sie dazu ein Repository).public class Foo { public EntityStatus EntityStatus { get; set; } } public enum EntityStatus { Unmodified, Modified, Added }
Ein Beispiel finden Sie unter folgendem Link: https://www.safaribooksonline.com/library/view/programming-entity-framework/9781449331825/ch04s06.html
quelle
Ich führe einen Windows-Dienst aus, der die Werte jede Minute aktualisiert, und ich hatte das gleiche Problem. Ich habe versucht, die @ DavidSherrets-Lösung auszuführen, aber nach einigen Stunden wurde dies auch langsam. Meine Lösung bestand darin, einfach für jeden neuen Lauf einen neuen Kontext wie diesen zu erstellen. Einfach, aber es funktioniert.
_dbContext = new DbContext();
quelle
Ich bin gerade auf dieses Problem gestoßen und bin schließlich auf eine bessere Lösung für diejenigen gestoßen, die die typische .NET Core-Abhängigkeitsinjektion verwenden. Sie können für jede Operation einen DbContext mit Gültigkeitsbereich verwenden. Das wird zurückgesetzt,
DbContext.ChangeTracker
damitSaveChangesAsync()
die Überprüfung von Entitäten aus früheren Iterationen nicht ins Stocken gerät. Hier ist ein Beispiel für eine ASP.NET Core Controller-Methode:/// <summary> /// An endpoint that processes a batch of records. /// </summary> /// <param name="provider">The service provider to create scoped DbContexts. /// This is injected by DI per the FromServices attribute.</param> /// <param name="records">The batch of records.</param> public async Task<IActionResult> PostRecords( [FromServices] IServiceProvider provider, Record[] records) { // The service scope factory is used to create a scope per iteration var serviceScopeFactory = provider.GetRequiredService<IServiceScopeFactory>(); foreach (var record in records) { // At the end of the using block, scope.Dispose() will be called, // release the DbContext so it can be disposed/reset using (var scope = serviceScopeFactory.CreateScope()) { var context = scope.ServiceProvider.GetService<MainDbContext>(); // Query and modify database records as needed await context.SaveChangesAsync(); } } return Ok(); }
Da ASP.NET Core-Projekte normalerweise DbContextPool verwenden, werden die DbContext-Objekte nicht einmal erstellt / zerstört. (Falls Sie interessiert waren, ruft DbContextPool tatsächlich auf
DbContext.ResetState()
undDbContext.Resurrect()
, aber ich würde nicht empfehlen, diese direkt aus Ihrem Code heraus aufzurufen, da sie sich wahrscheinlich in zukünftigen Versionen ändern werden.) Https://github.com/aspnet/EntityFrameworkCore/blob/v2 .2.1 / src / EFCore / Internal / DbContextPool.cs # L157quelle
Ab EF Core 3.0 gibt es eine interne API , die den ChangeTracker zurücksetzen kann. Verwenden Sie dies nicht im Produktionscode, ich erwähne es, da es je nach Szenario jemandem beim Testen helfen kann.
using Microsoft.EntityFrameworkCore.Internal; _context.GetDependencies().StateManager.ResetState();
Wie der Kommentar zum Code sagt;
quelle
Nun, meine Meinung ist, dass EF oder ein Orm meiner Erfahrung nach unter zu viel Druck oder komplexem Modell nicht gut funktioniert.
Wenn du nicht verfolgen willst, würde ich wirklich sagen, warum überhaupt orm?
Wenn Geschwindigkeit die Hauptkraft ist, geht nichts über gespeicherte Prozeduren und eine gute Indizierung.
Und darüber hinaus, wenn Ihre Abfragen immer pro ID sind, sollten Sie eine nosql oder vielleicht sql mit nur key und json verwenden. Dies würde das Impedanzproblem zwischen Klassen und Tabellen vermeiden.
In Ihrem Fall erscheint mir das Laden von Objekten in Objekte auf diese Weise sehr langsam. In Ihrem Fall sind gespeicherte Prozeduren wirklich besser, da Sie den Datentransport durch das Netzwerk vermeiden und SQL viel schneller und optimierter ist, um die Aggregation und ähnliche Dinge zu verwalten.
quelle