2012-11-05 15 views
45

Obecnie pracuję nad projektem wykorzystującym najnowszą wersję Entity Framework i natknąłem się na problem, którego nie mogę rozwiązać.Struktura obiektu, problemy z aktualizacją powiązanych obiektów

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

W poniższym przykładzie mam klasy o nazwie Foo, który przechowuje różne właściwości, z 2 tych będących przypadkach innych klas

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żywać poniższego Edycja metody, mijam w obiekcie I Chciałbym zaktualizować i mogę sprawić, że Nazwa będzie poprawnie aktualizowana, ale 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 moim 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 mile widziane.

UPDATE: podstawie komentarzem Slauma zmodyfikowałem moje metody

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; 
} 

Po uruchomieniu tego teraz, pojawia się błąd

typu jednostka Collection`1 nie jest częścią modelu dla bieżącego kontekstu .

Aby spróbować obejść ten problem, dodałem kod, aby spróbować dołączyć podklasy newFoo do kontekstu, ale przez błąd mówiąc, że ObjectManager już podmiot taki sam

Obiekt z tym samym kluczem już istnieje w ObjectStateManager. ObjectStateManager nie może śledzić wiele obiektów o tych samych kluczowych

Odpowiedz

77

CurrentValues.SetValues aktualizacje tylko właściwości skalarnych ale bez podmiotami powiązanymi, więc trzeba zrobić to samo dla każdego z podmiotem powiązanym:

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żeli związek mógł zostały usunięte całkowicie lub zostały utworzone, musisz również jawnie obsługiwać te sprawy:

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; 
} 

Konieczne jest również ustawienie stanu podłączonych jednostek na Modified, jeśli relacja została zmieniona i również właściwości skalarne.

Edit

Jeśli - zgodnie z Twojego komentarza - Foo.SubFoo jest w rzeczywistości zbiorem i nie tylko odniesienie trzeba będzie coś podobnego do aktualizacji związanych 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; 
} 
+0

Dzięki za komentarz, mam posted an update na moje pytanie – Thewads

+0

@Thewads: Mogę sobie tylko wyobrazić, że masz to „* Kolekcja nie jest ... część modelu ... * "błąd, gdy' Foo.SubFoo' jest typem * kolekcji *. Ale jest to tylko odwołanie 'public SubFoo SubFoo {get; set} 'w twoim przykładowym modelu, a nie kolekcji. Czy to naprawdę nie jest kolekcja? Moja odpowiedź była oparta na bieżącym modelu, w którym 'SubFoo' nie jest zbiorem. – Slauma

+0

Niestety, nie zdawałem sobie sprawy, że model mający kolekcje ma znaczenie ... mój błąd. W modelu, z którym mam do czynienia: public ICollection SubFoo {get; set;} – Thewads

0

Można również zadzwonić 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(); 
Powiązane problemy