2012-11-05 3 views
45

Attualmente sto lavorando a un progetto utilizzando l'ultima versione di Entity Framework e ho riscontrato un problema che non riesco a risolvere.Quadro delle entità, problemi di aggiornamento degli oggetti correlati

Quando si tratta di aggiornare oggetti esistenti, posso abbastanza facilmente aggiornare le proprietà dell'oggetto ok, fino a quando si tratta di una proprietà che è un riferimento a un'altra classe.

Nell'esempio sotto Ho una classe denominata Foo, che memorizza varie proprietà, con 2 di queste essendo istanze di altre classi

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

Quando uso il metodo Modifica di seguito, passo nell'oggetto I desidero aggiornare e posso riuscire a far aggiornare correttamente il nome, tuttavia non sono riuscito a trovare un modo per ottenere le proprietà del SubFoo da modificare. Ad esempio, se la classe SubFoo ha una proprietà di Name, e questo è stato cambiato ed è diverso tra il mio DB e il newFoo, non viene aggiornato.

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

Qualsiasi aiuto o suggerimento sarebbe molto apprezzato.

UPDATE: Sulla base del commento di Slauma ho modificato il mio metodo di

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

Quando si esegue questa ora, ottengo l'errore

Il tipo di entità Collection`1 non fa parte del modello per il contesto attuale .

per cercare di ottenere intorno a questo, ho aggiunto il codice per cercare di fissare le sottoclassi newFoo al contesto, ma questo attraverso un errore che dice che l'ObjectManager aveva già un'entità stesso

Un oggetto con la stessa chiave esiste già in ObjectStateManager. L'ObjectStateManager non può tenere traccia più oggetti con gli stessi chiave

risposta

77

CurrentValues.SetValues aggiorna solo le proprietà scalari ma non enti collegati, quindi è necessario fare lo stesso per ogni entità correlate:

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

Se il rapporto potrebbe sono stati rimossi del tutto o sono stati creati è anche necessario per gestire questi casi in modo esplicito:

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

Alla fine è anche necessario impostare lo stato delle entità allegate su Modified se la relazione è stata modificata e anche le proprietà scalari.

Modifica

Se - in base al tuo commento - Foo.SubFoo è in realtà una collezione e non solo un riferimento avrete bisogno di qualcosa di simile per aggiornare le entità correlate:

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

Grazie per il commento, ho postato un aggiornamento nella mia interrogazione – Thewads

+0

@Thewads: Posso solo immaginare che si ottiene questo "Collection * ... non è parte del modello ... * "errore quando' Foo.SubFoo' è un tipo * collection *. Ma è solo un riferimento 'public SubFoo SubFoo {get; imposta} 'nel modello di esempio, non in una raccolta. Non è davvero una collezione? La mia risposta era basata sul modello attuale in cui "SubFoo" non è una collezione. – Slauma

+0

Scusate, non mi ero reso conto che il modello con le collezioni avrebbe fatto la differenza ... il mio errore. Nel modello con cui sto trattando: public ICollection SubFoo {get; set;} – Thewads

0

È possibile anche chiamare le tabelle in modo indipendente

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