Framework d’entité, problèmes de mise à jour des objects associés

Je travaille actuellement sur un projet utilisant la dernière version d’Entity Framework et j’ai rencontré un problème que je n’arrive pas à résoudre.

Lorsqu’il s’agit de mettre à jour des objects existants, je peux assez facilement mettre à jour les propriétés de l’object jusqu’à ce qu’il s’agisse d’une propriété faisant référence à une autre classe.

Dans l’exemple ci-dessous, j’ai une classe appelée Foo, qui stocke diverses propriétés, dont 2 étant des instances d’autres classes.

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

Lorsque j’utilise la méthode Edit() ci-dessous, je passe l’object que je souhaite mettre à jour et je parviens à obtenir le Name correctement mis à jour, mais je n’ai pas réussi à trouver un moyen d’obtenir les propriétés du SubFoo. changement. Par exemple, si la classe SubFoo a une propriété de Name , et que celle-ci a été modifiée et est différente entre mon DB et le newFoo , elle n’est pas mise à jour.

 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(dbFoo); entry.OriginalValues.SetValues(dbFoo); entry.CurrentValues.SetValues(newFoo); context.SaveChanges(); return newFoo; } 

Toute aide ou pointeur serait grandement apprécié.

UPDATE: Basé sur le commentaire de Slauma, j’ai modifié ma méthode pour

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

En cours d’exécution maintenant, je reçois l’erreur:

Le type d’entité Collection`1 ne fait pas partie du modèle pour le contexte actuel.

Pour essayer de contourner ce newFoo , j’ai ajouté du code pour essayer d’attacher les sous-classes newFoo au contexte, mais ceci par une erreur indiquant que l’ ObjectManager avait déjà une entité identique:

Un object avec la même clé existe déjà dans ObjectStateManager. ObjectStateManager ne peut pas suivre plusieurs objects avec la même clé

CurrentValues.SetValues ne met à jour que les propriétés scalaires mais aucune entité associée. Vous devez donc faire la même chose pour chaque entité liée:

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

Si la relation a pu être supprimée ou si elle a été créée, vous devez également gérer ces cas explicitement:

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

Vous devrez éventuellement également définir l’état des entités attachées sur Modified si la relation a été modifiée et les propriétés scalaires également.

modifier

Si – selon votre commentaire – Foo.SubFoo est en fait une collection et pas seulement une référence, vous aurez besoin de quelque chose comme ça pour mettre à jour les entités liées:

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

Je pensais juste que je publierais le lien ci-dessous car cela m’a vraiment aidé à comprendre comment mettre à jour les entités liées.

Mise à jour des données associées avec la structure d’entité dans une application ASP net mvc

REMARQUE: J’ai légèrement modifié la logique affichée dans la fonction UpdateInstructorCourses pour répondre à mes besoins.

Vous pouvez également appeler les tables indépendamment

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