Wie aktualisiere ich einen Datensatz mit dem Entity Framework Core?


79

Was ist der beste Ansatz zum Aktualisieren von Datenbanktabellendaten im Entity-Frame-Work-Core?

  1. Rufen Sie die Tabellenzeile erneut auf, nehmen Sie die Änderungen vor und speichern Sie sie
  2. Verwenden Sie das Schlüsselwort Update im Datenbankkontext und behandeln Sie die Ausnahme für nicht vorhandenes Element

Welche verbesserten Funktionen können wir gegenüber EF6 verwenden?

Antworten:


93

Dies ist der logische Prozess, um eine Entität mit Entity Framework Core zu aktualisieren:

  1. Instanz für DbContextKlasse erstellen
  2. Entität per Schlüssel abrufen
  3. Nehmen Sie Änderungen an den Eigenschaften der Entität vor
  4. Änderungen speichern

Update()Methode in DbContext:

Beginnt mit der Verfolgung der angegebenen Entität im Status "Geändert", sodass sie beim SaveChanges()Aufruf in der Datenbank aktualisiert wird.

Die Aktualisierungsmethode speichert keine Änderungen in der Datenbank. Stattdessen werden Zustände für Einträge in der DbContext-Instanz festgelegt.

Daher können wir die Update()Methode vorher aufrufen , um Änderungen in der Datenbank zu speichern.

Ich gehe von einigen Objektdefinitionen aus, um Ihre Frage zu beantworten:

  1. Der Datenbankname lautet Store

  2. Tabellenname ist Produkt

Produktklassendefinition:

public class Product
{
    public int? ProductID { get; set; }
    
    public string ProductName { get; set; }
    
    public string Description { get; set; }
    
    public decimal? UnitPrice { get; set; }
}

DbContext-Klassendefinition:

public class StoreDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Your Connection String");

        base.OnConfiguring(optionsBuilder);
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Order>(entity =>
        {
            // Set key for entity
            entity.HasKey(p => p.ProductID);
        });
        
        base.OnModelCreating(modelBuilder);
    }
}

Logik zum Aktualisieren der Entität:

using (var context = new StoreDbContext())
{
        // Retrieve entity by id
        // Answer for question #1
        var entity = context.Products.FirstOrDefault(item => item.ProductID == id);
        
        // Validate entity is not null
        if (entity != null)
        {
            // Answer for question #2

            // Make changes on entity
            entity.UnitPrice = 49.99m;
            entity.Description = "Collector's edition";
            
            /* If the entry is being tracked, then invoking update API is not needed. 
              The API only needs to be invoked if the entry was not tracked. 
              https://www.learnentityframeworkcore.com/dbcontext/modifying-data */
            // context.Products.Update(entity);
            
            // Save changes in database
            context.SaveChanges();
        }
}

1
Danke . Dies ist ein gutes Beispiel für die Verwendung von 2. Verwenden Sie das Schlüsselwort Update im Datenbankkontext und behandeln Sie die Ausnahme für nicht vorhandenes Element. Ich bin mehr daran interessiert, welches als Best Practice auszuwählen.
Charith

Warum haben Sie nullable int?für verwendet ProductID? Wird es optionaler Primärschlüssel?
Geldorientierter Programmierer

7
Tatsächlich ist der Zeilenkontext.Products.Update redundant, da die Entität nachverfolgt wird, sobald Sie sie aus dem Kontext abrufen. Hier ist ein ausgezeichneter Überblick über die verschiedenen Ansätze: learnentityframeworkcore.com/dbcontext/modifying-data
Johan Maes

62

Laut Microsoft-Dokumenten :

Der Read-First-Ansatz erfordert ein zusätzliches Lesen der Datenbank und kann zu komplexerem Code für die Behandlung von Parallelitätskonflikten führen

Sie sollten jedoch wissen, dass die Verwendung der Update-Methode in DbContext alle Felder als geändert markiert und alle in die Abfrage einbezieht . Wenn Sie eine Teilmenge von Feldern aktualisieren möchten, sollten Sie die Attach-Methode verwenden und dann das gewünschte Feld als manuell geändert markieren .

context.Attach(person);
context.Entry(person).Property(p => p.Name).IsModified = true;
context.SaveChanges();

30
Nur eine kleine Ausgabe, jetzt gibt es eine stärker typisierte Version dieser API: context.Entry(person).Property(p => p.Name).IsModified = true;
Guru Stron

2
Kann auch einfach tuncontext.Entry(person).State = EntityState.Modified;
Renan Coelho

1
Was bedeutet dieser Kontext? Eintrag (Person) .State = EntityState.Modified; bedeuten? Wenn ich mehrere Felder ändere, sollte ich Änderungen an dieser Zeile vornehmen?
user989988

1
Guter Punkt ehsan jan! ; D Wenn die Entität vom Benutzer aktualisiert wird und Sie nicht wissen, welche Felder geändert werden, können Sie _context.Attach(person).State = EntityState.Modified; angeben, dass diese Entität bei der SaveChanges-Methode aktualisiert werden soll.
Serpooshan

Großer Fang. Dies erfordert also, dass der Kontext "bleibt", damit er über Entitäten im Kontext Bescheid weiß. Ich habe das gerade getestet ... und wenn Sie versuchen, ein nicht vorhandenes zu bearbeiten, gibt es eine Ausnahme! :) Ein oder mehrere Fehler sind aufgetreten. (Es wurde versucht, eine Entität zu aktualisieren oder zu löschen, die nicht im Speicher vorhanden ist.)
granadaCoder

29
public async Task<bool> Update(MyObject item)
{
    Context.Entry(await Context.MyDbSet.FirstOrDefaultAsync(x => x.Id == item.Id)).CurrentValues.SetValues(item);
    return (await Context.SaveChangesAsync()) > 0;
}

Ich denke, diese Methode funktioniert wirklich gut, aber ich glaube, der Beitrag ist unten, weil er mehr Kontext hätte verwenden können. aber mag es!
Herr Kraus

1
Ich mag es auch! @Herr. Krause, wäre die Post nicht (bisher) niedrig, weil sie nur ein paar Tage alt ist?
Wellspring

1
Ich erhalte die Meldung "Verletzung der PRIMARY KEY-Einschränkung 'PK_Offer'. Es kann kein doppelter Schlüssel in das Objekt 'dbo.Offer' eingefügt werden."
Magnus Karlsson

7

Es ist super einfach

using (var dbContext = new DbContextBuilder().BuildDbContext())
{
    dbContext.Update(entity);
    await dbContext.SaveChangesAsync();
}

6

Microsoft Docs bietet zwei Ansätze.

Empfohlener HttpPost-Bearbeitungscode: Lesen und aktualisieren

Dies ist die gleiche alte Methode, die wir in früheren Versionen von Entity Framework verwendet haben. und das empfiehlt uns Microsoft.

Vorteile

  • Verhindert Überbuchungen
  • Die automatische Änderungsverfolgung von EF setzt das ModifiedFlag für die Felder, die durch Formulareingabe geändert werden.

Alternativer HttpPost-Code bearbeiten: Erstellen und anhängen

Eine Alternative besteht darin, eine vom Modellordner erstellte Entität an den EF-Kontext anzuhängen und als geändert zu markieren.

Wie in der anderen Antwort erwähnt, erfordert der Read-First-Ansatz ein zusätzliches Lesen der Datenbank und kann zu komplexerem Code für die Behandlung von Parallelitätskonflikten führen.


2

Nachdem ich alle Antworten durchgesehen hatte, dachte ich, ich werde zwei einfache Optionen hinzufügen

  1. Wenn Sie bereits mit FirstOrDefault () mit aktivierter Verfolgung auf den Datensatz zugegriffen haben (ohne die Funktion .AsNoTracking () zu verwenden, da dadurch die Verfolgung deaktiviert wird) und einige Felder aktualisiert haben, können Sie einfach context.SaveChanges () aufrufen.

  2. In einem anderen Fall haben Sie entweder eine Entität mit HtppPost auf den Server gebucht oder die Verfolgung aus irgendeinem Grund deaktiviert. Dann sollten Sie context.Update (entityName) vor context.SaveChanges () aufrufen.

Die erste Option aktualisiert nur die von Ihnen geänderten Felder, die zweite Option aktualisiert jedoch alle Felder in der Datenbank, obwohl keiner der Feldwerte tatsächlich aktualisiert wurde :)


0

Ein allgemeinerer Ansatz

Um diesen Ansatz zu vereinfachen, wird eine "ID" -Schnittstelle verwendet

public interface IGuidKey
{
    Guid Id { get; set; }
}

Die Hilfsmethode

    public static void Modify<T>(this DbSet<T> set, Guid id, Action<T> func)
        where T : class, IGuidKey, new()
    {
        var target = new T
        {
            Id = id
        };
        var entry = set.Attach(target);
        func(target);
        foreach (var property in entry.Properties)
        {
            var original = property.OriginalValue;
            var current = property.CurrentValue;

            if (ReferenceEquals(original, current))
            {
                continue;
            }

            if (original == null)
            {
                property.IsModified = true;
                continue;
            }

            var propertyIsModified = !original.Equals(current);
            property.IsModified = propertyIsModified;
        }
    }

Verwendung

dbContext.Operations.Modify(id, x => { x.Title = "aaa"; });
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.