6

Próbuję zaktualizować Customer w mojej bazie danych przy użyciu interfejsu ASP.NET Web API i Entity Framework 5, ale to nie jest pracujący. Moje jednostki wyglądają tak:Najpierw kod EF5 z ASP.NET Web API: Zaktualizuj encję ze związkiem wiele-do-wielu

public class CustomerModel 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 

    // More fields 

    public ICollection<CustomerTypeModel> CustomerTypes { get; set; } 
} 

public class CustomerTypeModel 
{ 
    public int Id { get; set; } 
    public string Type { get; set; } 

    [JsonIgnore] 
    public ICollection<CustomerModel> Customers { get; set; } 
} 

Nic tak wyjątkowego. Zbudowałem interfejs internetowy, w którym użytkownicy mogą dodawać klientów, podając nazwę i sprawdzając jeden lub więcej typów klientów. Po uderzeniu w przycisk, dane są wysyłane do mojej internetowej metoda API:

public void Put([FromBody]CustomerModel customer) 
{ 
    using (var context = new MyContext()) 
    { 
     context.Customers.Attach(customer); 
     context.Entry(customer).State = EntityState.Modified; 
     context.SaveChanges(); 
    } 
} 

ten aktualizuje pola klientów, ale związane z nimi rodzaje klientów są ignorowane. Przychodzące customer przedmiot ma zawierać listę CustomerTypes powinno być związane z:

[0] => { Id: 1, Type: "Finance", Customers: Null }, 
[1] => { Id: 2, Type: "Insurance", Customers: Null } 
[2] => { Id: 3, Type: "Electronics", Customers: Null } 

Ale zamiast patrzenia na tej liście i dodawanie/usuwanie podmioty powiązane, EF prostu ignoruje go. Nowe skojarzenia są ignorowane, a istniejące powiązania pozostają, nawet jeśli powinny zostać usunięte.

Miałem podobny problem podczas wstawiania klienta do bazy danych, zostało to naprawione, gdy dostosowałem stan tych podmiotów do EntityState.Unchanged. Oczywiście starałem się zastosować tę samą magiczną poprawkę w moim scenariuszu aktualizacja:

public void Put([FromBody]CustomerModel customer) 
{ 
    using (var context = new MyContext()) 
    { 
     foreach (var customertype in customer.CustomerTypes) 
     { 
      context.Entry(customertype).State = EntityState.Unchanged; 
     } 

     context.Customers.Attach(customer); 
     context.Entry(customer).State = EntityState.Modified; 
     context.SaveChanges(); 
    } 
} 

Ale EF ciągle wyświetla ten sam problem.

Wszelkie pomysły na rozwiązanie tego problemu? Czy naprawdę powinienem wyczyścić instrukcję ręcznie na liście CustomerTypes, a następnie ręcznie je dodać?

Z góry dziękuję.

JP

Odpowiedz

10

Nie można tego rozwiązać, ustawiając tylko stany encji. Najpierw należy załadować klienta z bazy danych, w tym wszystkie jego bieżące typy, a następnie usunąć typy lub dodać typy do załadowanego klienta zgodnie ze zaktualizowaną kolekcją typów wysłanego klienta. Śledzenie zmian zrobi resztę usunąć wpisy z tabeli łączącej lub wstawić nowe pozycje:

public void Put([FromBody]CustomerModel customer) 
{ 
    using (var context = new MyContext()) 
    { 
     var customerInDb = context.Customers.Include(c => c.CustomerTypes) 
      .Single(c => c.Id == customer.Id); 

     // Updates the Name property 
     context.Entry(customerInDb).CurrentValues.SetValues(customer); 

     // Remove types 
     foreach (var typeInDb in customerInDb.CustomerTypes.ToList()) 
      if (!customer.CustomerTypes.Any(t => t.Id == typeInDb.Id)) 
       customerInDb.CustomerTypes.Remove(typeInDb); 

     // Add new types 
     foreach (var type in customer.CustomerTypes) 
      if (!customerInDb.CustomerTypes.Any(t => t.Id == type.Id)) 
      { 
       context.CustomerTypes.Attach(type); 
       customerInDb.CustomerTypes.Add(type); 
      } 

     context.SaveChanges(); 
    } 
} 
+0

Tak, bałem się tego podręcznika sposób robienia rzeczy. Dzięki za kod, zadziałało jak urok i uratowało mnie kilka prób zmagania się ze stanami :) –

+0

Czy istnieje sposób na zrobienie tego generycznego, aby działał dla każdej własności nawigacji? – CodyK

+0

@Codemiester: Możesz przyjrzeć się bibliotece GraphDiff wspomnianej w komentarzach tutaj: http://entityframework.codeplex.com/workitem/864 – Slauma

0

Czystsze rozwiązaniem byłoby:

public void Put([FromBody]CustomerModel customer) 
{ 
    using (var context = new MyContext()) 
    { 
     var customerInDb = context.Customers.Include(c => c.CustomerTypes) 
      .Single(c => c.Id == customer.Id); 

     // Updates the Name property 
     context.Entry(customerInDb).CurrentValues.SetValues(customer); 

     // Remove types 
     customer.CustomerTypes.Clear(); 

     // Add new types 
     foreach (var type in customer.CustomerTypes) 
     { 
      context.CustomerTypes.Attach(type); 
      customerInDb.CustomerTypes.Add(type); 
     } 

     context.SaveChanges(); 
    } 
} 
+0

Czy istnieje sposób na zrobienie tego generycznego, aby działał dla dowolnej właściwości nawigacji? – CodyK