So fügen Sie untergeordnete Entitäten hinzu/aktualisieren, wenn Sie eine übergeordnete Entität in EF aktualisieren

So fügen Sie untergeordnete Entitäten hinzu/aktualisieren, wenn Sie eine übergeordnete Entität in EF aktualisieren

Da das Modell, das an den WebApi-Controller gesendet wird, von jedem Entity-Framework (EF)-Kontext getrennt ist, besteht die einzige Option darin, das Objektdiagramm (übergeordnetes Element einschließlich seiner untergeordneten Elemente) aus der Datenbank zu laden und zu vergleichen, welche untergeordneten Elemente hinzugefügt, gelöscht oder hinzugefügt wurden Aktualisiert. (Es sei denn, Sie würden die Änderungen mit Ihrem eigenen Tracking-Mechanismus während des getrennten Zustands (im Browser oder wo auch immer) verfolgen, was meiner Meinung nach komplexer ist als das Folgende.) Es könnte so aussehen:

public void Update(UpdateParentModel model)
{
    var existingParent = _dbContext.Parents
        .Where(p => p.Id == model.Id)
        .Include(p => p.Children)
        .SingleOrDefault();

    if (existingParent != null)
    {
        // Update parent
        _dbContext.Entry(existingParent).CurrentValues.SetValues(model);

        // Delete children
        foreach (var existingChild in existingParent.Children.ToList())
        {
            if (!model.Children.Any(c => c.Id == existingChild.Id))
                _dbContext.Children.Remove(existingChild);
        }

        // Update and Insert children
        foreach (var childModel in model.Children)
        {
            var existingChild = existingParent.Children
                .Where(c => c.Id == childModel.Id && c.Id != default(int))
                .SingleOrDefault();

            if (existingChild != null)
                // Update child
                _dbContext.Entry(existingChild).CurrentValues.SetValues(childModel);
            else
            {
                // Insert child
                var newChild = new Child
                {
                    Data = childModel.Data,
                    //...
                };
                existingParent.Children.Add(newChild);
            }
        }

        _dbContext.SaveChanges();
    }
}

...CurrentValues.SetValues kann jedes Objekt annehmen und Eigenschaftswerte basierend auf dem Eigenschaftsnamen der angehängten Entität zuordnen. Wenn sich die Eigenschaftsnamen in Ihrem Modell von den Namen in der Entität unterscheiden, können Sie diese Methode nicht verwenden und müssen die Werte einzeln zuweisen.


Okay Leute. Ich hatte diese Antwort einmal, verlor sie aber auf dem Weg. absolute Folter, wenn Sie wissen, dass es einen besseren Weg gibt, sich aber nicht daran erinnern oder ihn finden können! Es ist sehr einfach. Ich habe es gerade mehrfach getestet.

var parent = _dbContext.Parents
  .Where(p => p.Id == model.Id)
  .Include(p => p.Children)
  .FirstOrDefault();

parent.Children = _dbContext.Children.Where(c => <Query for New List Here>);
_dbContext.Entry(parent).State = EntityState.Modified;

_dbContext.SaveChanges();

Sie können die gesamte Liste durch eine neue ersetzen! Der SQL-Code entfernt und fügt Entitäten nach Bedarf hinzu. Damit brauchen Sie sich nicht zu beschäftigen. Stellen Sie sicher, dass Sie die Kindersammlung oder keine Würfel enthalten. Viel Glück!


Ich habe mit so etwas herumgespielt...

protected void UpdateChildCollection<Tparent, Tid , Tchild>(Tparent dbItem, Tparent newItem, Func<Tparent, IEnumerable<Tchild>> selector, Func<Tchild, Tid> idSelector) where Tchild : class
    {
        var dbItems = selector(dbItem).ToList();
        var newItems = selector(newItem).ToList();

        if (dbItems == null && newItems == null)
            return;

        var original = dbItems?.ToDictionary(idSelector) ?? new Dictionary<Tid, Tchild>();
        var updated = newItems?.ToDictionary(idSelector) ?? new Dictionary<Tid, Tchild>();

        var toRemove = original.Where(i => !updated.ContainsKey(i.Key)).ToArray();
        var removed = toRemove.Select(i => DbContext.Entry(i.Value).State = EntityState.Deleted).ToArray();

        var toUpdate = original.Where(i => updated.ContainsKey(i.Key)).ToList();
        toUpdate.ForEach(i => DbContext.Entry(i.Value).CurrentValues.SetValues(updated[i.Key]));

        var toAdd = updated.Where(i => !original.ContainsKey(i.Key)).ToList();
        toAdd.ForEach(i => DbContext.Set<Tchild>().Add(i.Value));
    }

die Sie mit etwas wie:

aufrufen können
UpdateChildCollection(dbCopy, detached, p => p.MyCollectionProp, collectionItem => collectionItem.Id)

Leider fällt dies irgendwie um, wenn Sammlungseigenschaften für den untergeordneten Typ vorhanden sind, die ebenfalls aktualisiert werden müssen. Versuchen Sie, dies zu lösen, indem Sie ein IRepository (mit grundlegenden CRUD-Methoden) übergeben, das für den Aufruf von UpdateChildCollection selbst verantwortlich wäre. Würde das Repo anstelle von direkten Aufrufen von DbContext.Entry aufrufen.

Ich habe keine Ahnung, wie das alles im Maßstab funktionieren wird, bin mir aber nicht sicher, was ich sonst mit diesem Problem tun soll.