J'obtiens cette erreur lorsque je GetById () sur une entité, puis que je définis la collection d'entités enfants sur ma nouvelle liste qui provient de la vue MVC.
L'opération a échoué: la relation n'a pas pu être modifiée car une ou plusieurs propriétés de clé étrangère ne peuvent pas être Null. Lorsqu'une modification est apportée à une relation, la propriété de clé étrangère associée est définie sur une valeur nulle. Si la clé étrangère ne prend pas en charge les valeurs nulles, une nouvelle relation doit être définie, la propriété de clé étrangère doit se voir affecter une autre valeur non nulle ou l'objet non lié doit être supprimé.
Je ne comprends pas très bien cette ligne:
La relation n'a pas pu être modifiée car une ou plusieurs propriétés de clé étrangère ne peuvent pas être Null.
Pourquoi changerais-je la relation entre 2 entités? Il doit rester le même pendant toute la durée de vie de l'ensemble de l'application.
Le code sur lequel l'exception se produit consiste simplement à attribuer des classes enfants modifiées dans une collection à la classe parent existante. Nous espérons que cela permettrait la suppression des classes enfants, l'ajout de nouvelles classes et les modifications. J'aurais pensé qu'Entity Framework gère cela.
Les lignes de code peuvent être distillées pour:
var thisParent = _repo.GetById(1);
thisParent.ChildItems = modifiedParent.ChildItems();
_repo.Save();
Réponses:
Vous devez supprimer
thisParent.ChildItems
manuellement les anciens éléments enfants un par un. Entity Framework ne fait pas cela pour vous. Il ne peut finalement pas décider ce que vous voulez faire avec les anciens éléments enfants - si vous voulez les jeter ou si vous voulez les conserver et les affecter à d'autres entités parentes. Vous devez informer Entity Framework de votre décision. Mais vous DEVEZ prendre l'une de ces deux décisions car les entités enfants ne peuvent pas vivre seules sans référence à un parent dans la base de données (en raison de la contrainte de clé étrangère). C'est essentiellement ce que dit l'exception.Éditer
Ce que je ferais si des éléments enfants pouvaient être ajoutés, mis à jour et supprimés:
Remarque: ceci n'est pas testé. Cela suppose que la collection d'éléments enfants est de type
ICollection
. (J'ai régulièrementIList
et le code est un peu différent.) J'ai également supprimé toutes les abstractions du référentiel pour rester simple.Je ne sais pas si c'est une bonne solution, mais je pense qu'un travail acharné dans ce sens doit être fait pour prendre en charge toutes sortes de changements dans la collection de navigation. Je serais également heureux de voir un moyen plus simple de le faire.
la source
La raison pour laquelle vous êtes confronté à cela est due à la différence entre la composition et l' agrégation .
En composition, l'objet enfant est créé lorsque le parent est créé et est détruit lorsque son parent est détruit . Sa durée de vie est donc contrôlée par son parent. Par exemple, un article de blog et ses commentaires. Si un message est supprimé, ses commentaires doivent être supprimés. Cela n'a pas de sens d'avoir des commentaires pour un article qui n'existe pas. Idem pour les commandes et les articles de commande.
Dans l'agrégation, l'objet enfant peut exister quel que soit son parent . Si le parent est détruit, l'objet enfant peut toujours exister, car il peut être ajouté ultérieurement à un autre parent. par exemple: la relation entre une playlist et les chansons de cette playlist. Si la liste de lecture est supprimée, les chansons ne doivent pas être supprimées. Ils peuvent être ajoutés à une liste de lecture différente.
La façon dont Entity Framework différencie les relations d'agrégation et de composition est la suivante:
Pour la composition: il s'attend à ce que l'objet enfant ait une clé primaire composite (ParentID, ChildID). C'est par conception car les identifiants des enfants doivent être à la portée de leurs parents.
Pour l'agrégation: il s'attend à ce que la propriété de clé étrangère dans l'objet enfant soit Nullable.
Donc, la raison pour laquelle vous rencontrez ce problème est la façon dont vous avez défini votre clé primaire dans votre table enfant. Cela devrait être composite, mais ce n'est pas le cas. Ainsi, Entity Framework considère cette association comme une agrégation, ce qui signifie que lorsque vous supprimez ou effacez les objets enfants, elle ne supprimera pas les enregistrements enfants. Il supprimera simplement l'association et définira la colonne de clé étrangère correspondante sur NULL (afin que ces enregistrements enfants puissent plus tard être associés à un parent différent). Étant donné que votre colonne n'autorise pas NULL, vous obtenez l'exception que vous avez mentionnée.
Solutions:
1- Si vous avez une bonne raison de ne pas vouloir utiliser de clé composite, vous devez supprimer explicitement les objets enfants. Et cela peut être fait plus simple que les solutions suggérées précédemment:
2- Sinon, en définissant la clé primaire appropriée sur votre table enfant, votre code aura l'air plus significatif:
la source
C'est un très gros problème. Voici ce qui se passe réellement dans votre code:
Parent
partir de la base de données et obtenez une entité attachéeDésormais, la solution dépend vraiment de ce que vous voulez faire et comment aimeriez-vous le faire?
Si vous utilisez ASP.NET MVC, vous pouvez essayer d'utiliser UpdateModel ou TryUpdateModel .
Si vous voulez simplement mettre à jour manuellement les enfants existants, vous pouvez simplement faire quelque chose comme:
L'attachement n'est en fait pas nécessaire (définir l'état pour
Modified
attacher également l'entité) mais je l'aime parce que cela rend le processus plus évident.Si vous souhaitez modifier l'existant, supprimer l'existant et insérer de nouveaux enfants, vous devez faire quelque chose comme:
la source
.Clone()
. Avez-vous à l'esprit le cas où aChildItem
a d'autres propriétés de navigation sous-enfant? Mais dans ce cas, ne voudrions-nous pas que tout le sous-graphe soit attaché au contexte puisque nous nous attendrions à ce que tous les sous-enfants soient de nouveaux objets si l'enfant lui-même est nouveau? (Eh bien, cela peut être différent d'un modèle à l'autre, mais supposons que les sous-enfants soient "dépendants" de l'enfant comme les enfants sont dépendants du parent.)http://stackoverflow.com/questions/20233994/do-i-need-to-create-a-dbset-for-every-table-so-that-i-can-persist-child-entitie
J'ai trouvé cette réponse beaucoup plus utile pour la même erreur. Il semble que EF ne l'aime pas lorsque vous supprimez, il préfère Supprimer.
Vous pouvez supprimer une collection d'enregistrements attachés à un enregistrement comme celui-ci.
Dans l'exemple, tous les enregistrements de détail attachés à une commande ont leur état défini sur Supprimer. (En préparation de l'ajout des détails mis à jour, dans le cadre d'une mise à jour de la commande)
la source
Je ne sais pas pourquoi les deux autres réponses sont si populaires!
Je pense que vous aviez raison de supposer que le cadre ORM devrait le gérer - après tout, c'est ce qu'il promet de livrer. Sinon, votre modèle de domaine est corrompu par des problèmes de persistance. NHibernate gère cela avec plaisir si vous configurez correctement les paramètres de cascade. Dans Entity Framework, c'est également possible, ils s'attendent simplement à ce que vous suiviez de meilleures normes lors de la configuration de votre modèle de base de données, en particulier lorsqu'ils doivent déduire ce qui doit être fait en cascade:
Vous devez définir correctement la relation parent-enfant en utilisant une " relation d'identification ".
Si vous faites cela, Entity Framework sait que l'objet enfant est identifié par le parent et qu'il doit donc s'agir d'une situation "cascade-suppression-orphelins".
Autre que ce qui précède, vous devrez peut- être (de l'expérience NHibernate)
au lieu de remplacer entièrement la liste.
METTRE À JOUR
Le commentaire de @ Slauma m'a rappelé que les entités détachées sont une autre partie du problème global. Pour résoudre ce problème, vous pouvez adopter l'approche consistant à utiliser un classeur de modèles personnalisé qui construit vos modèles en essayant de le charger à partir du contexte. Ce billet de blog montre un exemple de ce que je veux dire.
la source
parent.ChildItems.Remove
place_dbContext.ChildItems.Remove
. Il n'y a toujours (EF <= 6) pas de prise en charge intégrée d'EF pour éviter un code long comme celui des autres réponses.return context.Items.Find(id) ?? new Item()
Si vous utilisez AutoMapper avec Entity Framework sur la même classe, vous pouvez rencontrer ce problème. Par exemple, si votre classe est
Cela essaiera de copier les deux propriétés. Dans ce cas, ClassBId est non Nullable. Étant donné qu'AutoMapper copiera,
destination.ClassB = input.ClassB;
cela posera un problème.Définissez votre AutoMapper sur la
ClassB
propriété Ignorer .la source
J'ai juste eu la même erreur. J'ai deux tables avec une relation parent-enfant, mais j'ai configuré une "sur suppression en cascade" sur la colonne de clé étrangère dans la définition de table de la table enfant. Ainsi, lorsque je supprime manuellement la ligne parent (via SQL) dans la base de données, il supprimera automatiquement les lignes enfants.
Cependant, cela n'a pas fonctionné dans EF, l'erreur décrite dans ce fil est apparue. La raison en était que dans mon modèle de données d'entité (fichier edmx), les propriétés de l'association entre le parent et la table enfant n'étaient pas correctes. L'
End1 OnDelete
option a été configurée pour êtrenone
("End1" dans mon modèle est la fin qui a une multiplicité de 1).J'ai changé manuellement l'
End1 OnDelete
optionCascade
et que cela a fonctionné. Je ne sais pas pourquoi EF n'est pas capable de récupérer cela, lorsque je mets à jour le modèle à partir de la base de données (j'ai un premier modèle de base de données).Pour être complet, voici à quoi ressemble mon code à supprimer:
Si je n'avais pas défini de suppression en cascade, je devrais supprimer les lignes enfants manuellement avant de supprimer la ligne parent.
la source
Cela se produit car l'entité enfant est marquée comme modifiée au lieu de supprimée.
Et la modification apportée par EF à l'entité enfant lors de
parent.Remove(child)
son exécution consiste simplement à définir la référence à son parent surnull
.Vous pouvez vérifier l'EntityState de l'enfant en tapant le code suivant dans la fenêtre d'exécution de Visual Studio lorsque l'exception se produit, après l'exécution
SaveChanges()
:où X doit être remplacé par l'entité supprimée.
Si vous n'avez pas accès à
ObjectContext
pour exécuter_context.ChildEntity.Remove(child)
, vous pouvez résoudre ce problème en intégrant la clé étrangère à la clé primaire de la table enfant.De cette façon, si vous exécutez
parent.Remove(child)
, EF marquera correctement l'entité comme supprimée.la source
Ce type de solution a fait l'affaire pour moi:
Il est important de dire que cela supprime tous les enregistrements et les réinsère. Mais pour mon cas (moins de 10) ça va.
J'espère que cela aide.
la source
J'ai rencontré ce problème aujourd'hui et je voulais partager ma solution. Dans mon cas, la solution était de supprimer les éléments enfants avant d'obtenir le parent de la base de données.
Auparavant, je le faisais comme dans le code ci-dessous. J'obtiendrai alors la même erreur répertoriée dans cette question.
Ce qui a fonctionné pour moi, c'est d'obtenir d'abord les éléments enfants, en utilisant le parentId (clé étrangère), puis de supprimer ces éléments. Ensuite, je peux obtenir le parent de la base de données et à ce stade, il ne devrait plus avoir d'éléments enfants et je peux ajouter de nouveaux éléments enfants.
la source
Vous devez effacer manuellement la collection ChildItems et y ajouter de nouveaux éléments:
Après cela, vous pouvez appeler la méthode d'extension DeleteOrphans qui gérera les entités orphelines (elle doit être appelée entre les méthodes DetectChanges et SaveChanges).
la source
context.DetectChanges();
.J'ai essayé ces solutions et bien d'autres, mais aucune d'elles n'a vraiment fonctionné. Puisqu'il s'agit de la première réponse sur google, j'ajouterai ma solution ici.
La méthode qui a bien fonctionné pour moi était de supprimer les relations de l'image pendant les commits, donc il n'y avait rien à foutre pour EF. Je l'ai fait en retrouvant l'objet parent dans le DBContext et en le supprimant. Puisque les propriétés de navigation de l'objet retrouvé sont toutes nulles, les relations des enfants sont ignorées lors de la validation.
Notez que cela suppose que les clés étrangères sont configurées avec ON DELETE CASCADE, donc lorsque la ligne parente est supprimée, les enfants seront nettoyés par la base de données.
la source
J'ai utilisé la solution de Mosh , mais il ne m'était pas évident de savoir comment implémenter correctement la clé de composition dans le code en premier.
Voici donc la solution:
la source
J'ai eu le même problème, mais je savais que cela avait bien fonctionné dans d'autres cas, alors j'ai réduit le problème à ceci:
Tout ce que j'avais à faire était de faire de ParentId une partie de PK composite pour indiquer que les enfants ne peuvent pas exister sans un parent. J'ai utilisé le modèle DB-first, ajouté le PK et marqué la colonne parentId comme EntityKey (j'ai donc dû la mettre à jour à la fois dans DB et EF - je ne sais pas si EF seul suffirait).
Une fois que vous y réfléchissez, c'est une distinction très élégante qu'EF utilise pour décider si les enfants "ont un sens" sans parent (dans ce cas, Clear () ne les supprimera pas et ne lèvera pas d'exception à moins que vous ne définissiez le ParentId sur autre chose / spécial ), ou - comme dans la question d'origine - nous nous attendons à ce que les éléments soient supprimés une fois qu'ils sont supprimés du parent.
la source
Ce problème se produit parce que nous essayons de supprimer la table parent encore les données de la table enfant sont présentes. Nous résolvons le problème avec l'aide de la suppression en cascade.
Dans le modèle Create, méthode dans la classe dbcontext.
Après cela, dans notre appel API
L' option de suppression en cascade supprime le parent ainsi que la table enfant liée au parent avec ce code simple. Faites-le essayer de cette manière simple.
Supprimer la plage utilisée pour supprimer la liste des enregistrements dans la base de données Merci
la source
J'ai également résolu mon problème avec la réponse de Mosh et je pensais que la réponse de PeterB était un peu car elle utilisait une enum comme clé étrangère. N'oubliez pas que vous devrez ajouter une nouvelle migration après avoir ajouté ce code.
Je peux également recommander cet article de blog pour d'autres solutions:
http://www.kianryan.co.uk/2013/03/orphaned-child/
Code:
la source
En utilisant la solution de Slauma, j'ai créé des fonctions génériques pour aider à mettre à jour les objets enfants et les collections d'objets enfants.
Tous mes objets persistants implémentent cette interface
Avec cela, j'ai implémenté ces deux fonctions dans mon référentiel
Pour l'utiliser, je fais ce qui suit:
J'espère que cela t'aides
EXTRA: Vous pouvez également créer une classe DbContextExtentions séparée (ou votre propre inferface de contexte):
et utilisez-le comme:
la source
J'ai été confronté au même problème lorsque je vais supprimer mon enregistrement qu'un problème s'est produit, car la solution de ce problème est que lorsque vous allez supprimer votre enregistrement, il vous manque quelque chose avant de supprimer l'en-tête / l'enregistrement principal, vous devez écrire dans le code pour supprimer ses détails avant l'en-tête / maître J'espère que votre problème sera résolu.
la source
Si vous utilisez Auto mapper et que le problème suivant est la bonne solution, cela fonctionne pour moi
https://www.codeproject.com/Articles/576393/Solutionplusto-aplus-Theplusoperationplusfailed
Étant donné que le problème est que nous mappons des propriétés de navigation nulles et que nous n'avons en fait pas besoin de les mettre à jour sur l'entité puisqu'elles n'ont pas changé sur le contrat, nous devons les ignorer dans la définition de mappage:
Donc mon code s'est terminé comme ceci:
la source
J'ai rencontré ce problème avant plusieurs heures et j'ai tout essayé, mais dans mon cas, la solution était différente de la liste ci-dessus.
Si vous utilisez une entité déjà extraite de la base de données et essayez de modifier ses enfants, l'erreur se produira, mais si vous obtenez une nouvelle copie de l'entité de la base de données, il ne devrait y avoir aucun problème. N'utilisez pas ceci:
Utilisez ceci:
la source