Aktualisieren Sie nur geänderte Felder in Entity Framework

117272
Narayana

Ich arbeite an einer Website zu ASP.NET MVC4 und EF5. Ich möchte sicherstellen, dass nur geänderte Werte in der Datenbank aktualisiert werden. Ich verwende ein Arbeitselement und Repositorys für die Datenarbeit. Hier ist der Code für ein generisches Repository. Ich habe zwei Fragen und brauche eine Codeüberprüfung für den folgenden Code.

  1. Ist der Code zum Aktualisieren nur geänderte Felder gut? Es funktioniert, aber ich weiß nicht, ob dies der beste Weg ist. Ich mag es besonders nicht, dass ich diese Schleife selbst durch die Eigenschaften ziehen muss (kann EF das nicht?). Ich mag auch nicht die Besetzung und den Vergleich.

  2. Wie sind meine Methoden zum Hinzufügen und Aktualisieren? Ich plane, dass die Clients (MVC / API-Controller) einen Dienst verwenden (eine Arbeitseinheit übergeben), um die Arbeit zu erledigen. Sie werden das Repository nicht direkt verwenden. Das Repository hat keine Save () - Methode, nur das UnitOfWork hat eine. Ich kann innerhalb des Dienstes sicherstellen, dass ich denselben Kontext zum Aktualisieren / Hinzufügen von Entitäten verwenden werde. Gibt es vor diesem Hintergrund Szenarien, in denen der Code zum Hinzufügen / Aktualisieren fehlschlägt?

public class EFRepository<T> : IRepository<T> where T : class
{
    protected DbContext DbContext { get; set; }
    protected DbSet<T> DbSet { get; set; }

    public EFRepository(DbContext dbContext)
    {
        if (dbContext == null)
            throw new ArgumentNullException("dbContext");
        DbContext = dbContext;
        DbSet = DbContext.Set<T>();
    }

    public virtual void Add(T entity)
    {
        DbContext.Entry(entity).State = EntityState.Added;
    }

    public virtual void Update(T entity)
    {
        //dbEntityEntry.State = EntityState.Modified; --- I cannot do this.

        //Ensure only modified fields are updated.
        var dbEntityEntry = DbContext.Entry(entity);
        foreach (var property in dbEntityEntry.OriginalValues.PropertyNames)
        {
            var original = dbEntityEntry.OriginalValues.GetValue<object>(property);
            var current = dbEntityEntry.CurrentValues.GetValue<object>(property);
            if (original != null && !original.Equals(current))
                dbEntityEntry.Property(property).IsModified = true;
        }
    }

    //... Other methods ...
}
Antworten
27
Dies setzt also voraus, dass die T-Entität nicht bereits ein Objekt ist, das bereits direkt aus dem dbcontext-Objekt abgerufen wurde. dreza vor 6 Jahren 0
Ja, T ist nicht so etwas wie dbconext.Persons.First (). T ist eine von Ef automatisch generierte Domänenklasse. Eine Instanz dieses Typs wird normalerweise aus dem Ansichtsmodell erstellt. Narayana vor 6 Jahren 0
Gibt es einen Grund, warum Sie die Entität nicht direkt erhalten und nur das manipulieren würden? Dann müssen Sie sich keine Sorgen machen, eine Update-Methode aufzurufen. Vielleicht könnten Sie so etwas wie AutoMapper verwenden, um von Ihrem Ansichtsmodell zu Modell abzubilden? Nur Gedanken dreza vor 6 Jahren 0
Mein schlechtes, ich rufe vor dem Update eine FindById () auf, da ich nur die Id aus der View bekomme. Nachdem ich die VM dem Objekt zugeordnet habe, rufe ich dieses Update () auf. es kommt also doch aus dem gleichen Kontext. Oh, willst du damit sagen, dass ich nicht mal Entity-State setzen muss? Narayana vor 6 Jahren 0
Wenn Sie das Objekt aus Ihrem Kontext abrufen, modifizieren Sie diese Eigenschaften und rufen dann SaveChanges in Ihrem Kontext auf. Dadurch wird bereits eine Aktualisierung für dieses Modell durchgeführt. EF erledigt das für Sie. Ist das dein Verständnis? dreza vor 6 Jahren 4
@dreza Ich habe den Grund für diesen Beitrag (http://msdn.microsoft.com/de-in/data/jj592676.aspx) falsch verstanden. Ich habe das "getrennte" Szenario falsch verstanden. Ich machte einige Experimente und stellte fest, dass alles perfekt funktioniert, solange ich alles innerhalb des gleichen Kontextumfangs verwende. Nur geänderte Eigenschaften werden an die Datenbank gesendet. Das Hinzufügen und Aktualisieren wird von EF selbst vorgenommen. Vielen Dank! Narayana vor 6 Jahren 0
Nur ein Update, ich habe mich komplett von EF entfernt und bin jetzt komplett auf Dapper. Narayana vor 3 Jahren 1

3 Antworten auf die Frage

22
Max

Warum sollte man nachprüfen, ob der Wert / die Eigenschaft geändert wurde? Sie fügen einfach ohne zusätzlichen Grund zusätzliche Komplexität hinzu. Sicher könnte es etwas sein! Schneller, um nur geänderte Werte zu aktualisieren, aber im Gegenzug fügen Sie einen Overhead hinzu, um die geänderten Werte zu verfolgen, wodurch die Leistungssteigerung verringert wird.

Rufen Sie einfach Updatedas gesamte Objekt auf ... Alle Werte, die nicht geändert wurden, bleiben gleich und die geänderten Werte werden aktualisiert.

Wenn Sie nach Geschwindigkeit suchen, suchen Sie wahrscheinlich am falschen Ort, an dem Sie MS auspressen.

Ich denke aus Sicht der DB. Wenn ich alle Felder in einer Zeile aktualisiere, werden * ALL * meine Indizes in dieser Tabelle aktualisiert. Okay, höchstwahrscheinlich habe ich diese Logik nicht für alle Entitäten, aber in bestimmten Tabellen muss ich diese Option haben. Narayana vor 6 Jahren 13
@ Narayana, weil ein großer Teil der C # -Community (Hinweis: NICHT C # selbst) jeden bestraft, der es wagt, in Sachen Leistung zu denken. Das ist ein biiiig nein nein. Ja, du hast recht, ich bin ein bisschen abgestumpft. Nicholas Petersen vor 3 Jahren 1
Und was ist, wenn sich nach all den Überprüfungen herausstellt, dass überhaupt keine Aktualisierungen erforderlich sind und wir den gesamten Roundtrip in der Datenbank speichern können. Ganz zu schweigen von einer möglichen Ungültigmachung von Caches und wie von @ Narayana erwähnt, die Indizes neu aufzubauen. Steves vor 2 Jahren 1
Um die Idee zu erweitern, dass das Aktualisieren von Feldern in der Datenbank ohne Änderungen erfolgt, sollten Sie (wenn auch schlecht geschriebene) Trigger berücksichtigen, die IF UPDATE (FieldName) prüfen und dann die Logik ausführen. Wenn EF das unveränderte Feld in der Aktualisierungsanweisung enthält, wird es bei der Überprüfung von IF UPDATE (FieldName) auf "Wahr" gesetzt und führt wahrscheinlich eine unnötige Logik aus. Chris Porter vor 2 Jahren 1
6
brainless coder

Wenn Sie sich Sorgen machen, dass die Aktualisierungsanweisung die Abfrage verlangsamen könnte, würde ich einen etwas anderen Ansatz vorschlagen. Sie können Ihren Code so ändern, dass er in die Liste der geänderten Eigenschaften passt. Wenn Sie also in Szenarien wissen, welche Eigenschaft aktualisiert wird, haben Sie die Möglichkeit, diese noch schneller zu gestalten, und auch, wenn keine Eigenschaft bereitgestellt wird. Dann können Sie alle aktualisieren -

public virtual void Update(T entity, params Expression<Func<T, object>>[] updatedProperties)
{
    //dbEntityEntry.State = EntityState.Modified; --- I cannot do this.

    //Ensure only modified fields are updated.
    var dbEntityEntry = DbContext.Entry(entity);
    if (updatedProperties.Any())
    {
        //update explicitly mentioned properties
        foreach (var property in updatedProperties)
        {
            dbEntityEntry.Property(property).IsModified = true;
        }
    }else{
        //no items mentioned, so find out the updated entries
        foreach (var property in dbEntityEntry.OriginalValues.PropertyNames)
        {
            var original = dbEntityEntry.OriginalValues.GetValue<object>(property);
            var current = dbEntityEntry.CurrentValues.GetValue<object>(property);
            if (original != null && !original.Equals(current))
                dbEntityEntry.Property(property).IsModified = true;
        }
    }
}

Dieser Ansatz gibt Ihnen die Freiheit, die beiden folgenden Möglichkeiten zu erfüllen:

_repository.Update(obj);

und auch

_repository.Update(obj, o => o.Name, o => o.Description);

Beachten Sie auch, ich nehme an params. So können Sie beliebig viele Eigenschaften verketten.

Ich schätze diese Idee, weil sie sehr hilfreich ist, aber ich habe Schwierigkeiten, die "else" -Klausel zu implementieren, die die aktualisierten Einträge findet. Jedes Mal, wenn meine Originalwerte mit meinen CurrentValues ​​für jede Eigenschaft übereinstimmen. Woher kennt es die ursprünglichen Werte? Josh vor 3 Jahren 0
1
sdmcnitt

I am just learning EF but I would worry about concurrent users with your update.

When you call SaveChanges it would be best if EF will only update the fields that are changed to minimize the chance for collisions or overwriting the changes of a concurrent user.

Hallo, ich habe am Ende alles in einem Kontext erledigt, also kümmert sich EF darum. Ich habe jetzt keine Szenarien getrennt, was bedeutet, dass ich keine modifizierten oder hinzugefügten Zustände explizit gesetzt habe. Ich lasse mich von EF kümmern. Narayana vor 5 Jahren 0
Wenn Sie also die Änderungsnachverfolgung für die Entitäten verwenden, wird EF vermutlich ein Update nur für die fehlerhaften Spalten generieren. Aber ich weiß, dass es auch umgangen oder außer Kraft gesetzt werden kann (wie zum Beispiel Anhängen nach dem Setzen von Feldwerten usw.). Ich wollte nur sicherstellen, dass der Code von jedem darauf aufpasst. sdmcnitt vor 5 Jahren 1
@sdmcnitt dann sollte das wohl ein Kommentar sein ... Vogel612 vor 5 Jahren 0