896 Stimmen

Entity Framework 5 Aktualisieren eines Datensatzes

Ich habe verschiedene Methoden zum Bearbeiten/Aktualisieren eines Datensatzes innerhalb von Entity Framework 5 in einer ASP.NET MVC3-Umgebung untersucht, aber bisher erfüllt keine von ihnen alle Anforderungen, die ich habe. Ich werde erklären, warum.

Ich habe drei Methoden gefunden, zu denen ich die Vor- und Nachteile nennen werde:

Methode 1 - Originaldatensatz laden, jede Eigenschaft aktualisieren

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    original.BusinessEntityId = updatedUser.BusinessEntityId;
    original.Email = updatedUser.Email;
    original.EmployeeId = updatedUser.EmployeeId;
    original.Forename = updatedUser.Forename;
    original.Surname = updatedUser.Surname;
    original.Telephone = updatedUser.Telephone;
    original.Title = updatedUser.Title;
    original.Fax = updatedUser.Fax;
    original.ASPNetUserId = updatedUser.ASPNetUserId;
    db.SaveChanges();
}    

Profis

  • Kann angeben, welche Eigenschaften sich ändern
  • Ansichten müssen nicht jede Eigenschaft enthalten

Nachteile

  • 2 x Abfragen der Datenbank, um das Original zu laden und dann zu aktualisieren

Methode 2 - Originaldatensatz laden, geänderte Werte einstellen

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    db.Entry(original).CurrentValues.SetValues(updatedUser);
    db.SaveChanges();
}

Profis

  • Nur geänderte Eigenschaften werden an die Datenbank gesendet

Nachteile

  • Ansichten müssen jede Eigenschaft enthalten
  • 2 x Abfragen der Datenbank, um das Original zu laden und dann zu aktualisieren

Methode 3 - Aktualisierten Datensatz anhängen und Status auf EntityState.Modified setzen

db.Users.Attach(updatedUser);
db.Entry(updatedUser).State = EntityState.Modified;
db.SaveChanges();

Profis

  • 1 x Abfrage der Datenbank zur Aktualisierung

Nachteile

  • Kann nicht angeben, welche Eigenschaften sich ändern
  • Ansichten müssen jede Eigenschaft enthalten

Pregunta

Meine Frage an euch: Gibt es einen sauberen Weg, wie ich diese Ziele erreichen kann?

  • Kann angeben, welche Eigenschaften sich ändern
  • Ansichten müssen nicht jede Eigenschaft enthalten (z. B. das Passwort!)
  • 1 x Abfrage der Datenbank zur Aktualisierung

Mir ist klar, dass dies nur eine Kleinigkeit ist, aber vielleicht übersehe ich eine einfache Lösung für dieses Problem. Wenn nicht, wird sich Methode eins durchsetzen ;-)

4voto

Arad Punkte 3591

EF Core 7.0 neue Funktion: ExecuteUpdate

Endlich! Nach langem Warten hat EF Core 7.0 jetzt eine nativ unterstützt Art zu laufen UPDATE (und auch DELETE ) Anweisungen, während Sie auch beliebige LINQ-Abfragen verwenden können ( .Where(u => ...) ), ohne dass die entsprechenden Entitäten erst aus der Datenbank abgerufen werden müssen: Die neue eingebaute Methode namens ExecuteUpdate - siehe "Was ist neu in EF Core 7.0?" .

ExecuteUpdate ist genau für diese Art von Szenarien gedacht, es kann auf jedem IQueryable Instanz und ermöglicht es Ihnen, bestimmte Spalten in einer beliebigen Anzahl von Zeilen zu aktualisieren, während Sie immer eine einzeln UPDATE Erklärung hinter den Kulissen, um sie so effizient wie möglich zu gestalten.

Verwendung:

Stellen Sie sich vor, Sie möchten die E-Mail und den Anzeigenamen eines bestimmten Benutzers aktualisieren:

dbContext.Users
    .Where(u => u.Id == someId)
    .ExecuteUpdate(b => b
        .SetProperty(u => u.Email, "NewEmail@gmail.com")
        .SetProperty(u => u.DisplayName, "New Display Name")
    );

Wie Sie sehen können, ExecuteUpdate müssen Sie einen oder mehrere Aufrufe an die SetProperty Methode, um anzugeben, welche Eigenschaft aktualisiert werden soll und welcher neue Wert ihr zugewiesen werden soll.

EF Core wird dies wie folgt umsetzen UPDATE Erklärung:

UPDATE [u]
    SET [u].[Email] = "NewEmail@gmail.com",
    [u].[DisplayName] = "New Display Name"
FROM [Users] AS [u]
WHERE [u].[Id] = someId

Auch, ExecuteDelete zum Löschen von Zeilen:

Es gibt auch ein Gegenstück zu ExecuteUpdate genannt. ExecuteDelete die, wie der Name schon sagt, verwendet werden kann, um eine einzelne oder mehrere Zeilen auf einmal zu löschen, ohne sie vorher zu holen.

Verwendung:

// Delete users that haven't been active in 2022:
dbContext.Users
    .Where(u => u.LastActiveAt.Year < 2022)
    .ExecuteDelete();

Ähnlich wie ExecuteUpdate , ExecuteDelete erzeugt DELETE SQL-Anweisungen hinter den Kulissen - in diesem Fall die folgende:

DELETE FROM [u]
FROM [Users] AS [u]
WHERE DATEPART(year, [u].[LastActiveAt]) < 2022

Sonstige Anmerkungen:

  • Denken Sie daran, dass beide ExecuteUpdate y ExecuteDelete sind "terminierend", d.h. die Aktualisierung/Löschung findet statt, sobald Sie die Methode aufrufen. Sie sollten nicht die Methode dbContext.SaveChanges() danach.
  • Wenn Sie neugierig sind auf die SetProperty Methode, und Sie sind verwirrt darüber, warum ExectueUpdate nicht stattdessen einen Initialisierungsausdruck für ein Mitglied erhält (z. B. .ExecuteUpdate(new User { Email = "..." }) , dann siehe dieser Kommentar (und die umliegenden) in der GitHub-Ausgabe für diese Funktion.
  • Falls Sie sich für die Gründe für die Namensgebung interessieren und wissen möchten, warum das Präfix Execute ausgewählt wurde (es gab auch andere Kandidaten), siehe dieser Kommentar und das vorangegangene (ziemlich lange) Gespräch.
  • Beide Methoden haben auch async Äquivalente, genannt ExecuteUpdateAsync und ExecuteDeleteAsync beziehungsweise.

2voto

Bostwick Punkte 653

Nur um die Liste der Möglichkeiten zu ergänzen. Sie können das Objekt auch aus der Datenbank holen und ein Auto-Mapping-Tool wie Auto Mapper um die Teile des Datensatzes zu aktualisieren, die Sie ändern möchten.

2voto

Chriss Punkte 71

Je nach Anwendungsfall sind alle oben genannten Lösungen möglich. Dies ist, wie ich in der Regel tun es jedoch:

Für serverseitigen Code (z. B. einen Batch-Prozess) lade ich normalerweise die Entitäten und arbeite mit dynamischen Proxys. Bei Batch-Prozessen müssen Sie die Daten in der Regel ohnehin zum Zeitpunkt der Ausführung des Dienstes laden. Ich versuche, die Daten per Batch zu laden, anstatt die Find-Methode zu verwenden, um Zeit zu sparen. Je nach Prozess verwende ich optimistische oder pessimistische Gleichzeitigkeitskontrolle (ich verwende immer optimistisch, außer bei parallelen Ausführungsszenarien, bei denen ich einige Datensätze mit einfachen SQL-Anweisungen sperren muss, was allerdings selten ist). Je nach Code und Szenario können die Auswirkungen auf fast Null reduziert werden.

Für clientseitige Szenarien haben Sie mehrere Möglichkeiten

  1. Verwenden Sie Ansichtsmodelle. Die Modelle sollten eine Eigenschaft UpdateStatus(unmodified-inserted-updated-deleted) haben. Es liegt in der Verantwortung des Clients, den richtigen Wert für diese Spalte zu setzen, abhängig von den Benutzeraktionen (Einfügen-Aktualisieren-Löschen). Der Server kann entweder die Datenbank nach den ursprünglichen Werten abfragen oder der Client sollte die ursprünglichen Werte zusammen mit den geänderten Zeilen an den Server senden. Der Server sollte die Originalwerte anhängen und die Spalte UpdateStatus für jede Zeile verwenden, um zu entscheiden, wie die neuen Werte zu behandeln sind. In diesem Szenario verwende ich immer optimistische Gleichzeitigkeit. Damit werden nur die Anweisungen zum Einfügen, Aktualisieren und Löschen ausgeführt und keine Selects, aber es könnte etwas cleverer Code erforderlich sein, um den Graphen zu durchlaufen und die Entitäten zu aktualisieren (hängt von Ihrem Szenario - Ihrer Anwendung - ab). Ein Mapper kann helfen, aber er kann die CRUD-Logik nicht verarbeiten.

  2. Verwenden Sie eine Bibliothek wie breeze.js, die einen Großteil dieser Komplexität verbirgt (wie unter 1 beschrieben), und versuchen Sie, sie an Ihren Anwendungsfall anzupassen.

Ich hoffe, es hilft

0voto

George Fabish Punkte 195

Es wurden bereits einige wirklich gute Antworten gegeben, aber ich wollte auch meinen Senf dazugeben. Hier ist eine sehr einfache Möglichkeit, ein View-Objekt in eine Entität zu konvertieren. Die einfache Idee ist, dass nur die Eigenschaften, die im View-Modell vorhanden sind, in die Entität geschrieben werden. Dies ist ähnlich wie die Antwort von @Anik Islam Abhi, hat aber eine Nullpropagation.

public static T MapVMUpdate<T>(object updatedVM, T original)
{
    PropertyInfo[] originalProps = original.GetType().GetProperties();
    PropertyInfo[] vmProps = updatedVM.GetType().GetProperties();
    foreach (PropertyInfo prop in vmProps)
    {
        PropertyInfo projectProp = originalProps.FirstOrDefault(x => x.Name == prop.Name);
        if (projectProp != null)
        {
            projectProp.SetValue(original, prop.GetValue(updatedVM));
        }
    }
    return original;
}

Profis

  • Die Ansichten müssen nicht alle Eigenschaften der Entität haben.
  • Sie müssen den Code nie aktualisieren, wenn Sie eine Eigenschaft zu einer Ansicht hinzufügen oder entfernen.
  • Vollständig generisch

Nachteile

  • 2 Zugriffe auf die Datenbank, einer zum Laden der ursprünglichen Entität, und einer zum Speichern derselben.

Für mich überwiegen die Einfachheit und die geringen Wartungsanforderungen dieses Ansatzes den zusätzlichen Datenbankaufruf.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X