web-dev-qa-db-fra.com

Comment supprimer un objet enfant dans NHibernate?

J'ai un objet parent qui a une relation un à plusieurs avec une IList d'objets enfants. Quelle est la meilleure façon de supprimer les objets enfants? Je ne supprime pas le parent. Mon objet parent contient une IList d'objets enfants. Voici le mappage de la relation un à plusieurs:

<bag name="Tiers" cascade="all">
  <key column="mismatch_id_no" />
  <one-to-many class="TGR_BL.PromoTier,TGR_BL"/>
</bag>

Si j'essaie de supprimer tous les objets de la collection à l'aide de clear (), puis d'appeler SaveOrUpdate (), j'obtiens cette exception:

System.Data.SqlClient.SqlException: Cannot insert the value NULL into column

Si j'essaie de supprimer les objets enfants individuellement puis de les supprimer du parent, j'obtiens une exception:

deleted object would be re-saved by cascade

C'est la première fois que je traite de la suppression d'objets enfants dans NHibernate. Qu'est-ce que je fais mal?

edit: Juste pour clarifier - je n'essaye PAS de supprimer l'objet parent, juste les objets enfants. J'ai la relation établie comme un à plusieurs sur le parent. Dois-je également créer une relation plusieurs-à-un sur le mappage d'objet enfant?

78
Mark Struzinski

Vous obtenez la première erreur car, lorsque vous supprimez les éléments de la collection, le mode de fonctionnement par défaut de NHibernate consiste simplement à rompre l'association. Dans la base de données, NHibernate essaie de définir la colonne de clé étrangère sur la ligne enfant sur null. Étant donné que vous n'autorisez pas les valeurs NULL dans cette colonne, SQL Server déclenche l'erreur. La suppression de la collection ne supprimera pas nécessairement l'objet enfant, mais une façon de le faire consiste à définir cascade = all-delete-Orphan. Cela informe NHibernate qu'il doit supprimer les lignes nouvellement orphelines au lieu de définir la colonne de clé étrangère.

Vous obtenez la deuxième erreur car lorsque vous appelez SaveOrUpdate NHibernate supprime d'abord tous les objets enfants. Ensuite, étant donné qu'aucune relation n'est marquée comme inverse, NHibernate essaie également de définir la colonne de clé étrangère dans votre table enfant sur null. Étant donné que les lignes ont déjà été supprimées, vous recevez la deuxième erreur. Vous devez définir inverse = true d'un côté de votre relation pour résoudre ce problème. Cela se fait généralement d'un côté (clé primaire ou parent). Si vous ne le faites pas, NHibernate fera les mises à jour appropriées pour chaque côté de la relation. Malheureusement, exécuter deux mises à jour n'est pas la chose appropriée à faire.

Vous devez toujours marquer un côté de vos relations comme le côté inverse. Selon la façon dont vous codez, vous pouvez ou non avoir besoin d'utiliser la mise en cascade. Si vous souhaitez profiter des suppressions uniques comme vous essayez de le faire en utilisant Clear (), vous devez définir votre cascade.

135
Chuck

Selon la réponse de Chuck, j'ai résolu mon problème en ajoutant Inverse = true dans le mappage côté parent:

Le message a de nombreux MessageSentTo:

[HasMany(typeof(MessageSentTo), Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Inverse = true)]
public IList<MessageSentTo> MessageSendTos
{
    get { return m_MessageSendTo; }
    set { m_MessageSendTo = value; }
}

J'utilise Castle ActiveRecord. Merci Chuck.

3
hanuman0503

Essayez d'utiliser merge () au lieu de saveOrUpdate (). Assurez-vous également que votre cascade est définie sur all-delete-Orphan et que votre relation parent-enfant est inversible (inverse = true sur le parent, puis un champ dans l'enfant qui est l'ID parent avec not-null = true) .

2
Elie

Dans notre exemple, nous avons des catégories avec de nombreux produits où un produit n'est pas annulable.

Vous pouvez contourner le problème en supprimant le produit et en le supprimant de la collection du parent avant le rinçage, mais nous recherchons toujours une meilleure solution à ce problème.

product = pRepo.GetByID(newProduct.ProductID);
product.Category.Products.Remove(product);
pRepo.Delete(product);

J'espère que ça aide quand même

2
Liath

Modifiez la valeur d'attribut en cascade de "tous" à "tous-supprimer-orphelin".

0
Thomas Tekavec