6

Sto tentando di aggiornare uno Customer nel mio database utilizzando l'API Web ASP.NET e il codice Entity Framework 5 prima, ma non è lavoro. Le mie entità sono:Codice EF5 prima con API Web ASP.NET: entità di aggiornamento con relazione molti-a-molti

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

Niente di speciale. Ho creato un'interfaccia web in cui gli utenti possono aggiungere un cliente fornendo il nome e controllando uno o più tipi di clienti. Quando esce il pulsante di invio, i dati vengono inviati al mio metodo API Web:

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

Questo aggiorna i campi dei clienti, ma i tipi di clienti connessi vengono ignorati. Il customer oggetto in entrata contiene un elenco di CustomerTypes dovrebbe essere associata a:

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

Ma invece di guardare questa lista e l'aggiunta/rimozione di entità associate, EF solo ignora. Le nuove associazioni vengono ignorate e le associazioni esistenti rimangono anche se dovrebbero essere cancellate.

Ho avuto un problema simile quando inserivo un cliente nel database, questo è stato corretto quando ho regolato lo stato di queste entità su EntityState.Unchanged. Naturalmente, ho cercato di applicare questa stessa correzione magia nel mio scenario di aggiornamento:

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

Ma EF continua a visualizzare lo stesso comportamento.

Qualche idea su come risolvere questo problema? O dovrei semplicemente fare un semplice clear nell'elenco di CustomerTypes e aggiungerli manualmente?

Grazie in anticipo.

JP

risposta

10

questo non è davvero risolvibile impostando solo gli stati di entità. È necessario caricare il cliente dal database prima includendo tutti i tipi correnti e quindi rimuovere i tipi da o aggiungere tipi al cliente caricato in base alla raccolta dei tipi aggiornati del cliente registrato. Il rilevamento delle modifiche farà il resto per eliminare le voci dalla tabella aderire o inserire nuove voci:

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

Sì, temevo questo modo manuale di fare le cose. Grazie per il codice, ha funzionato come un fascino e mi ha salvato un calvario che lotta con gli stati :) –

+0

C'è un modo per rendere questo generico così che funzioni per qualsiasi proprietà di navigazione? – CodyK

+0

@Codemiester: puoi dare un'occhiata alla libreria GraphDiff citata nei commenti qui: http://entityframework.codeplex.com/workitem/864 – Slauma

0

una soluzione più pulita potrebbe essere:

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

Esiste un modo per rendere questo generico in modo che funzioni per qualsiasi proprietà di navigazione? – CodyK