Je reçois l'erreur suivante de mon DbContext: "La contrainte de multiplicité a été violée. Le rôle 'MyEntity' de la relation 'MyModel.FK_ChildEntities_MyEntities' a la multiplicité 1 ou 0..1."
en utilisant ASP.NET, Entity Framework 4
Travailler avec une entité détachée
L'erreur se produit la deuxième fois que j'essaie de rattacher une entité au dbcontext. Le scénario est une sauvegarde infructueuse suivie d'une nouvelle tentative.
J'ai une entité détachée en session. L'utilisateur modifie les propriétés d'un formulaire, ajoute des éléments, supprime des éléments et clique sur Enregistrer. Je reçois une copie attachée de l'entité à partir d'une nouvelle instance de dbcontext, applique les modifications de l'entité détachée à l'entité attachée, valide, trouve une erreur et abandonne. L'utilisateur change ce qu'il veut et enregistre à nouveau.
Lors de la deuxième sauvegarde, tout le processus de sauvegarde se répète, mais cette fois, tout se passe bien. Presque tout est dupliqué, ce qui cause une erreur ou une autre ou la totalité d'entre eux. Les valeurs des vues et des tables de recherche supposées être uniquement des références sont créées et de nouveaux identifiants. La plupart de ces problèmes que j'ai pu résoudre, mais il me reste l'erreur de multiplicité. Les éléments enfants sont créés en tant que copies exactes d'autres éléments enfants, jusqu'à l'identifiant unique, uniquement dans l'état Added. Ou, si je fais référence à certaines propriétés, au lieu de cloner un enfant non modifié, il supprime le nouveau. Quoi qu'il en soit, aucun code ne s'exécute comme il l'avait fait la première fois.
Je supprime l'instance de dbcontext et l'entité attachée à chaque tentative de sauvegarde. Je pensais que cela suffirait pour annuler tout changement, mais il doit rester quelque chose. La seule chose qui n’a pas été repérée ou réinitialisée est l’entité détachée, qui est en session, mais je n’y apporte aucune modification. Du moins pas directement.
Le code (très simplifié) ressemble à ceci:
void Save()
{
using (var context = new MyContext())
{
// detached entity from session
MyEntity detachedEntity = (MyEntity)Session["DetachedEntity"];
// attached entity from context
MyEntity attachedEntity = context.MyEntities.Single(x=>x.id == detachedEntity.id);
// <remove children representing lookup table elements from detachedEntity to prevent duplicates>
// <remove children representing view elements from detachedEntity to prevent duplicates>
// <apply changes from detachedEntity to attachedEntity>
// <add new children>
// <remove deleted children>
// <update modified children>
// <set entity state to unchanged on view and lookup elements of attachedEntity to ensure no duplicates...>
// <validate>
if (errors.count>0)
// <report errors>
else
context.SaveChanges();
}
}
à titre d'exemple, cela génère une erreur de multiplicité:
// represents first save:
using (var context = new MyContext())
{
// detached entity from session
MyEntity detachedEntity = (MyEntity)Session["DetachedEntity"];
// attached entity from context
MyEntity attachedEntity = context.MyEntities.Single(x=>x.id == detachedEntity.id);
int debug1 = context.ChangeTracker.Entries<ChildEntity>().Count(); // debug1 == 0;
attachedEntity.ChildEntities.Add(detachedEntity.ChildEntities.First());
int debug2 = context.ChangeTracker.Entries<ChildEntity>().Count(); // debug2 == 1;
}
// represents second save:
using (var context = new MyContext())
{
// detached entity from session
MyEntity detachedEntity = (MyEntity)Session["DetachedEntity"];
// attached entity from context
MyEntity attachedEntity = context.MyEntities.Single(x=>x.id == detachedEntity.id);
int debug1 = context.ChangeTracker.Entries<ChildEntity>().Count(); // debug1 == 0;
attachedEntity.ChildEntities.Add(detachedEntity.ChildEntities.First());
int debug2 = context.ChangeTracker.Entries<ChildEntity>().Count(); // multiplicity error;
}
d'une manière ou d'une autre, dbcontext se souvient des objets qui y ont été ajoutés. si le même objet apparaît deux fois, il ... souffle
au lieu d'ajouter des entités enfants de mon entité détachée à celles attachées, j'aurais dû créer de nouvelles copies de chaque enfant
ChildEntity detachedChild = detachedEntity.ChildEntities.First();
attachedEntity.ChildEntities.Add(new ChildEntity {
propertyA = detachedChild.propertyA,
propertyB = detachedChild.propertyB
});
au lieu de
attachedEntity.ChildEntities.Add(detachedEntity.ChildEntities.First());
Le problème est que détachéeChild.parent doit être assignée joinParent.
foreach(var detachedEntity in detachedEntities)
{
attachedEntity.ChildEntities.Add(detachedEntity);
detachedEntity.ParentEntity = attachedEntity;
}
Assurez-vous d'inspecter les propriétés de l'objet que vous essayez d'ajouter. Dans mon cas, il s'agissait à tort de référencer le même objet non valide sur chaque ajout qui ne lui plaisait pas et a donc lancé la même erreur que vous avez ici.
J'ai rencontré cette erreur lorsque j'avais des propriétés de navigation non définies ou des propriétés de navigation appartenant au code incorrect First DBContext
J'ai résolu ce problème en rendant les collections enfants de l'entité parent virtuelle. Cela permet de mettre facilement à jour l'entité lorsque ses collections enfants ne changent pas, ce qui était pour moi la plupart du temps.
EF 6 Update
Pour moi, régler l’état de l’objet sur ajouté a fonctionné sur des sons
ChildEntity detachedChild = detachedEntity.ChildEntities.First();
var newChild = new ChildEntity {
propertyA = detachedChild.propertyA,
propertyB = detachedChild.propertyB
});
// Mark all added entity entity state to Added
attachedEntity.ChildEntities.Add(newChild );
db.Entry(newChild ).State = EntityState.Added;