Encja Framework, problemy z aktualizacją powiązanych obiektów

Obecnie pracuję nad projektem wykorzystującym najnowszą wersję Entity Framework i natknąłem się na problem, którego nie mogę rozwiązać.

Jeśli chodzi o aktualizację istniejących obiektów, mogę dość łatwo zaktualizować właściwości obiektu ok, dopóki nie dojdzie do właściwości, która jest odniesieniem do innej klasy.

W poniższym przykładzie mam klasę Foo, która przechowuje różne właściwości, z których 2 są instancjami innych klasy

public class Foo
{
     public int Id {get; set;}
     public string Name {get; set;}
     public SubFoo SubFoo {get; set}
     public AnotherSubFoo AnotherSubFoo {get; set}
}

Kiedy używam poniższej metody Edit(), przechodzę do obiektu, który chcę zaktualizować i udaje mi się uzyskać poprawną aktualizację Name, jednak nie udało mi się znaleźć sposobu na zmianę właściwości SubFoo. Na przykład, jeśli klasa SubFoo ma właściwość Name, która została zmieniona i różni się między my DB a newFoo, nie jest aktualizowana.

public Foo Edit(Foo newFoo)
{
    var dbFoo = context.Foo
        .Include(x => x.SubFoo)
        .Include(x => x.AnotherSubFoo)
        .Single(c => c.Id == newFoo.Id);

    var entry = context.Entry<Foo>(dbFoo);
    entry.OriginalValues.SetValues(dbFoo);
    entry.CurrentValues.SetValues(newFoo);

    context.SaveChanges();

    return newFoo;
}

Każda pomoc lub wskazówki byłyby znacznie doceniam to.

UPDATE: Na podstawie komentarza Slauma zmodyfikowałem swoją metodę na

public Foo Edit(Foo newFoo)
{
    var dbFoo = context.Foo
        .Include(x => x.SubFoo)
        .Include(x => x.AnotherSubFoo)
        .Single(c => c.Id == newFoo.Id);

    context.Entry(dbFoo).CurrentValues.SetValues(newFoo);
    context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo);

    context.SaveChanges();

    return newFoo;
}

Podczas uruchamiania tego teraz, dostaję błąd:

Zbiór typu entity ' 1 nie jest częścią modelu dla bieżącego kontekst.

Aby to obejść, dodałem kod, aby dołączyć newFoo podklasy do kontekstu, ale to przez błąd mówiący, że ObjectManager już miał encję taką samą:

Obiekt o tym samym key już istnieje w ObjectStateManager. Menedżer ObjectStateManager nie może śledzić wielu obiektów z tym samym klucz

Author: Massimiliano Kraus, 2012-11-05

3 answers

CurrentValues.SetValues aktualizuje tylko właściwości skalarne, ale nie ma powiązanych podmiotów, więc musisz zrobić to samo dla każdego powiązanego podmiotu:

public Foo Edit(Foo newFoo)
{
    var dbFoo = context.Foo
                       .Include(x => x.SubFoo)
                       .Include(x => x.AnotherSubFoo)
                       .Single(c => c.Id == newFoo.Id);

    context.Entry(dbFoo).CurrentValues.SetValues(newFoo);
    context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo);
    context.Entry(dbFoo.AnotherSubFoo).CurrentValues.SetValues(newFoo.AnotherSubFoo);

    context.SaveChanges();

    return newFoo;
}

Jeśli związek mógł zostać całkowicie usunięty lub został utworzony, musisz również zająć się tymi przypadkami jawnie:

public Foo Edit(Foo newFoo)
{
    var dbFoo = context.Foo
                       .Include(x => x.SubFoo)
                       .Include(x => x.AnotherSubFoo)
                       .Single(c => c.Id == newFoo.Id);

    context.Entry(dbFoo).CurrentValues.SetValues(newFoo);
    if (dbFoo.SubFoo != null)
    {
        if (newFoo.SubFoo != null)
        {
            if (dbFoo.SubFoo.Id == newFoo.SubFoo.Id)
                // no relationship change, only scalar prop.
                context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo);
            else
            {
                // Relationship change
                // Attach assumes that newFoo.SubFoo is an existing entity
                context.SubFoos.Attach(newFoo.SubFoo);
                dbFoo.SubFoo = newFoo.SubFoo;
            }
        }
        else // relationship has been removed
            dbFoo.SubFoo = null;
    }
    else
    {
        if (newFoo.SubFoo != null) // relationship has been added
        {
            // Attach assumes that newFoo.SubFoo is an existing entity
            context.SubFoos.Attach(newFoo.SubFoo);
            dbFoo.SubFoo = newFoo.SubFoo;
        }
        // else -> old and new SubFoo is null -> nothing to do
    }

    // the same logic for AnotherSubFoo ...

    context.SaveChanges();

    return newFoo;
}

W końcu musisz również ustawić stan dołączonych encji na Modified, jeśli relacja została zmieniona i również właściwości skalarne.

Edit

If-according to twój komentarz - {[5] } jest w rzeczywistości kolekcją i nie tylko referencją, będziesz potrzebował czegoś takiego, aby zaktualizować powiązane podmioty:

public Foo Edit(Foo newFoo)
{
    var dbFoo = context.Foo
                       .Include(x => x.SubFoo)
                       .Include(x => x.AnotherSubFoo)
                       .Single(c => c.Id == newFoo.Id);

    // Update foo (works only for scalar properties)
    context.Entry(dbFoo).CurrentValues.SetValues(newFoo);

    // Delete subFoos from database that are not in the newFoo.SubFoo collection
    foreach (var dbSubFoo in dbFoo.SubFoo.ToList())
        if (!newFoo.SubFoo.Any(s => s.Id == dbSubFoo.Id))
            context.SubFoos.Remove(dbSubFoo);

    foreach (var newSubFoo in newFoo.SubFoo)
    {
        var dbSubFoo = dbFoo.SubFoo.SingleOrDefault(s => s.Id == newSubFoo.Id);
        if (dbSubFoo != null)
            // Update subFoos that are in the newFoo.SubFoo collection
            context.Entry(dbSubFoo).CurrentValues.SetValues(newSubFoo);
        else
            // Insert subFoos into the database that are not
            // in the dbFoo.subFoo collection
            dbFoo.SubFoo.Add(newSubFoo);
    }

    // and the same for AnotherSubFoo...

    db.SaveChanges();

    return newFoo;
}
 87
Author: Slauma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-21 22:15:45

Po prostu pomyślałem, że zamieszczę link poniżej, ponieważ naprawdę pomógł mi zrozumieć, jak aktualizować powiązane podmioty.

Aktualizacja powiązanych danych z ramą entity w aplikacji asp net mvc

Uwaga: zmieniłem nieco logikę, która jest pokazana w funkcji UpdateInstructorCourses, aby zaspokoić moje potrzeby.

 3
Author: craigvl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-15 02:38:31

Można również wywoływać tabele niezależnie

MyContext db = new MyContext
// I like using asynchronous calls in my API methods 
var OldFoo = await db.Foo.FindAsync(id);
var OldAssociateFoo = db.AssociatedFoo;  
var NewFoo = OldFoo;
var NewAssociatedFoo = OldAssociatedFoo;

NewFoo.SomeValue = "The Value";
NewAssociatedFoo.OtherValue = 20;

db.Entry(OldFoo).CurrentValues.SetValues(NewFoo);
db.Entry(OldAssociatedFoo).CurrentValues.SetValues(NewAssociatedFoo);

await db.SaveChangesAsync();
 0
Author: iuppiter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-23 04:17:10