web-dev-qa-db-de.com

Wie kann ich verfolgte Entitäten im Entity-Framework löschen?

Ich führe einen Korrekturcode aus, der über einen großen Haufen von Entitäten läuft, während seine Geschwindigkeit abnimmt. Dies ist darauf zurückzuführen, dass die Anzahl der verfolgten Entitäten im Kontext mit jeder Iteration steigt. Es kann lange dauern, also speichere ich die Änderungen am Ende von jeder Iteration. Jede Iteration ist unabhängig und ändert die zuvor geladenen Entitäten nicht. 

Ich weiß, dass ich die Änderungsnachverfolgung deaktivieren kann, aber ich möchte dies nicht, da es sich nicht um einen Masseneinfügungscode handelt, sondern um die Entitäten zu laden und einige Dinge zu berechnen, und wenn die Zahlen nicht korrekt sind, die neuen Zahlen festlegen und aktualisieren/löschen/erstellen einige zusätzliche Entitäten. Ich weiß, dass ich für jede Iteration einen neuen DbContext erstellen kann, der wahrscheinlich schneller ablaufen würde, als alles in derselben Instanz auszuführen, aber ich denke, es könnte einen besseren Weg geben. 

Also ist die Frage; Gibt es eine Möglichkeit, die zuvor im DB-Kontext geladenen Entitäten zu löschen?

36
hazimdikenli

Sie können eine DbContext-Methode oder eine Erweiterungsmethode hinzufügen, die ChangeTracker zum Entfernen aller hinzugefügten, geänderten und gelöschten Entitäten verwendet:

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;
}
59
David Sherret

1. Möglichkeit: trennen Sie den Eintrag

dbContext.Entry(entity).State = EntityState.Detached;

Wenn Sie den Eintrag entfernen, stoppt 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 Kontexten

Möglicherweise möchten Sie den Status Ihrer Entität unabhängig voneinander steuern, sodass Sie getrennte Diagramme verwenden können. Fügen Sie eine Eigenschaft für den Entitätsstatus hinzu und wandeln Sie diesen Status in die dbContext.Entry(entity).State um, 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

11
road242

Ich verwende einen Windows-Dienst, der die Werte jede Minute aktualisiert, und habe das gleiche Problem. Ich habe versucht, @DavidSherrets-Lösung auszuführen, aber nach ein paar Stunden wurde dies auch langsam. Meine Lösung bestand darin, für jeden neuen Lauf einfach einen neuen Kontext wie diesen zu erstellen. Einfach, aber es funktioniert.

_dbContext = new DbContext();

8
Ogglas

Nun, meiner Meinung nach funktioniert EF, oder irgendein Orm zu sein, meiner Erfahrung nach nicht gut, wenn zu viel Druck ausgeübt wird oder ein komplexes Modell vorliegt.

Wenn Sie nicht verfolgen möchten, würde ich wirklich sagen, warum überhaupt orm?

Wenn Geschwindigkeit die Hauptkraft ist, gibt es nichts Schöneres als gespeicherte Prozeduren und eine gute Indizierung.

Und darüber hinaus, wenn Ihre Abfragen immer per ID sind, sollten Sie eine nosql oder vielleicht eine sql mit nur key und json verwenden. Dies würde das Impedanzproblem zwischen Klassen und Tabellen vermeiden.

In Ihrem Fall erscheint es mir sehr langsam, Dinge auf diese Weise in Objekte zu laden. In Ihrem Fall sind gespeicherte Prozeduren wirklich besser, da Sie den Transport von Daten durch das Netzwerk vermeiden und SQL viel schneller und für die Verwaltung von Aggregationen und ähnlichen Dingen optimiert ist.

1
Kat Lim Ruiz

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 jeden Vorgang einen bereichsbezogenen DbContext verwenden. Dadurch wird DbContext.ChangeTracker zurückgesetzt, so dass SaveChangesAsync() die Überprüfung von Entitäten aus vergangenen Iterationen nicht blockiert. Hier ist eine Beispielmethode für ASP.NET Core Controller:

    /// <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 dadurch nicht einmal erstellt oder zerstört. (Falls Sie interessiert waren, ruft DbContextPool tatsächlich DbContext.ResetState() und DbContext.Resurrect() auf. Ich würde jedoch nicht empfehlen, diese direkt aus Ihrem Code heraus aufzurufen, da sie sich in zukünftigen Versionen wahrscheinlich ändern werden.) https://github.com/ aspnet/EntityFrameworkCore/blob/v2.2.1/src/EFCore/Internal/DbContextPool.cs # L157

1
Matt