2011-11-01 12 views
27

Mam aplikację, w której można utworzyć nowy typ produktu i dodać do tego produktu niektóre składniki. Produkt i składniki są jednostkami zapisanymi w bazie danych. Podmiot produktu ma kolekcję składników.Jednostka 4.1 Aktualizacja istniejącej jednostki nadrzędnej za pomocą nowego elementu podrzędnego Jednostki

(wersja uproszczona)

public class Product 
    Public Sub New() 
    Me.Ingredients = New List(Of Ingredient)() 
    End Sub 

    Property Ingredients as ICollection(Of Ingredient) 
end class 

Kiedy zapisać produkt po raz pierwszy, wszystko idzie dobrze: po prostu dodać je do kontekstu i SaveChanges zadzwonić.

myDataContext.Products.Add(product) 
myDataContext.SaveChanges() 

Zarówno produkt (rodzic) i składniki (dzieci) są zapisane i powiązane ze sobą. Wszystko dobrze.

Jednak gdy dodaję/usuwam składnik do istniejącego produktu, zaczynam mieć problemy. Najpierw wyczyściłem istniejącą kolekcję składników w jednostce produktu, a następnie ponownie dodałem zaktualizowaną listę składników (nie używam ponownie składników, aby dodać chwilę). Następnie zmieniam stan encji produktu na zmodyfikowany i wywołuję zmiany składowania. Na stanie zmieniającym I pojawia się jednak wyjątek "Obiekt z tym samym kluczem już istnieje w ObjectStateManager".

myDataContext.Entry(product).State = EntityState.Modified 

Po „jakiś” Searching zorientowali się, że problemem jest to, że wszystkie składniki mają podstawowy klucz 0 (ponieważ nie są one jeszcze dodane) i po zmianie stanu jednostki dominującej (produkt), wszystkie elementy podrzędne (składniki) są dołączone do kontekstu za pomocą klucza 0, co powoduje problem, ponieważ klucze nie są już unikalne.

Szukałem rozwiązania, ale nie mogę wymyślić, jak rozwiązać ten problem. Próbowałem dodać składniki do kontekstu przed zmianą stanu, ale wtedy brakuje powiązania między produktem a składnikami ... Jak zaktualizować istniejącą jednostkę nadrzędną o nowe, jeszcze nie dodane elementy podrzędne?

Używam Entity Framework 4.1 i Code First.

Mam nadzieję, że możesz mi pomóc!

Odpowiedz

36

Najpierw wyczyść istniejącą kolekcję składników w produkcie o nazwie , a następnie ponownie zaktualizuję listę składników.

Cóż, jest to rodzaj brutalnego ataku, aby zaktualizować kolekcję dziecięcą. EF nie ma żadnej magii, aby zaktualizować dzieci - co oznacza: dodawanie nowych dzieci, usuwanie usuniętych dzieci, aktualizowanie istniejących dzieci - poprzez ustawienie tylko stanu nadrzędnego na Modified. Zasadniczo procedura ta zmusza do usuwania starych dzieci również z bazy danych i wstawić nowy, tak jak poniżej:

// product is the detached product with the detached new children collection 
using (var context = new MyContext()) 
{ 
    var productInDb = context.Products.Include(p => p.Ingredients) 
     .Single(p => p.Id == product.Id); 

    // Update scalar/complex properties of parent 
    context.Entry(productInDb).CurrentValues.SetValues(product); 

    foreach (var ingredient in productInDb.Ingredients.ToList()) 
     context.Ingredients.Remove(ingredient); 

    productInDb.Ingredients.Clear(); // not necessary probably 

    foreach (var ingredient in product.Ingredients) 
     productInDb.Ingredients.Add(ingredient); 

    context.SaveChanges(); 
} 

Im lepsza procedura jest aktualizacja kolekcję dzieci w pamięci bez usuwania wszystkich dzieci w bazie:

+0

Działa to absolutnie idealnie !! Wielkie dzięki! – DirkDooms

+0

Dużo czasu zajęło mi znalezienie prawidłowej aktualizacji jednostek. Dziękujemy za kontekst.Entry (productInDb) .CurrentValues.SetValues ​​(produkt); –

+0

To jest świetne, teraz muszę po prostu wymyślić, jak sprawić, by działało poprzez refleksję, dzięki czemu mogę przeglądać wszystkie kolekcje na jednostce, którą zapisuję. – Nikkoli

-2

po wielu miesiącach zmagających się ze zrozumieniem całej tej okropnej organizacji Entity Framework Mam nadzieję, że to pomoże komuś i nie przejdzie przez żadną frustrację, którą zniosłem.

public void SaveOrder(SaleOrder order) 
     { 
      using (var ctx = new CompanyContext()) 
      { 
       foreach (var orderDetail in order.SaleOrderDetails) 
       { 
        if(orderDetail.SaleOrderDetailId == default(int)) 
        { 
         orderDetail.SaleOrderId = order.SaleOrderId; 
         ctx.SaleOrderDetails.Add(orderDetail); 
        }else 
        { 
         ctx.Entry(orderDetail).State = EntityState.Modified; 
        } 
       } 

       ctx.Entry(order).State = order.SaleOrderId == default(int) ? EntityState.Added : EntityState.Modified; 
       ctx.SaveChanges();     

      } 

     } 
+0

wydaje mi się, że nie brałeś pod uwagę usuniętych informacji o zamówieniach –

4

Znalazłem niedawno article na przedłużeniu GraphDiff dla DbContext.

Podobno jest to ogólny, wielokrotnego użytku wariant Slauma 's solution.

Przykład Kod:

using (var context = new TestDbContext()) 
{ 
    // Update DBcompany and the collection the company and state that the company 'owns' the collection Contacts. 
    context.UpdateGraph(company, map => map.OwnedCollection(p => p.Contacts));  
    context.SaveChanges(); 
} 

Na marginesie; Widzę, że autor zaproponował zespołowi EF wykorzystanie swojego kodu w numerze #864 Provide better support for working with disconnected entities.

-1

Podejrzewam, to jest prostsze rozwiązanie.

public Individual 
{ 
..... 

public List<Address> Addresses{get;set;} 


} 

//where base.Update from Generic Repository 
public virtual void Update(T entity) 
     { 
      _dbset.Attach(entity); 
      _dataContext.Entry(entity).State = EntityState.Modified; 
     } 

//overridden update 
public override void Update(Individual entity) 
     { 


      var entry = this.DataContext.Entry(entity); 
      var key = Helper.GetPrimaryKey(entry); 
      var dbEntry = this.DataContext.Set<Individual>().Find(key); 

      if (entry.State == EntityState.Detached) 
      { 
       if (dbEntry != null) 
       { 
        var attachedEntry = this.DataContext.Entry(dbEntry); 
        attachedEntry.CurrentValues.SetValues(entity); 
       } 
       else 
       { 
        base.Update(entity); 
       } 
      } 
      else 
      { 
       base.Update(entity); 
      } 
      if (entity.Addresses.Count > 0) 
      { 
       foreach (var address in entity.Addresses) 
       { 
        if (address != null) 
        { 
         this.DataContext.Set<Address>().Attach(address); 
         DataContext.Entry(address).State = EntityState.Modified; 
        } 
       } 
      } 
     } 
Powiązane problemy