NHibernate Fluent Cascade Question - en Essayant D'Insérer la valeur NULL ID
J'ai les modèles suivants & mappages (extraits de code ci-dessous).
Un Concours a d'avoir plusieurs CompetitionAnswers associé (à choix multiples) dès le départ.
À l'heure actuelle, à l'aide de la fluidité de la mappages NHibernate indiqué ci-dessous, lorsque je crée une nouvelle marque de la Concurrence de l'objet, remplir les propriétés, puis créer 3 nouveaux CompetitionAnswer objets et les ajouter à la CompetitionAnswers propriété (propriété de la Concurrence), je m'attends à appeler la méthode Save sur la session qui vise à INSÉRER les 1 de la Concurrence de lignes et de 3 CompetitionAnswer lignes de la DB.
Cependant, dès que j'essaie d'appeler la méthode Save sur la session, il se plaint de ce que CompetitionId est nul et il ne peut pas insérer une valeur null dans la CompetitionAnswers table pour que le terrain qui est à droite, il ne devrait pas, cependant, je suppose que la NHibernate serait d'abord créer de la Compétition, alors utiliser la nouvelle valeur d'IDENTITÉ généré (CompetitionId) dans le CompetitionAnswers table?
De La Concurrence (Modèle)
public virtual int CompetitionId { get; private set; }
public virtual string Title { get; set; }
public virtual string Description { get; set; }
public virtual IList<CompetitionAnswer> CompetitionAnswers { get; set; }
CompetitionAnswer (Modèle)
public virtual int CompetitionAnswerId { get; set; }
public virtual string Answer { get; set; }
public virtual Competition Competition { get; set; }
CompetitionMap (NHibernate Fluent Cartographie)
public CompetitionMap()
{
Id(x => x.CompetitionId)
.GeneratedBy.Native();
Map(x => x.Title);
Map(x => x.Description);
HasMany(x => x.CompetitionAnswers)
.Cascade.AllDeleteOrphan()
.KeyColumn("CompetitionId")
.Inverse();
Table("Competitions");
}
CompetitionAnswerMap (NHibernate Fluent Cartographie)
public CompetitionAnswerMap()
{
Id(x => x.CompetitionAnswerId)
.GeneratedBy.Native();
Map(x => x.Answer);
References(x => x.Competition)
.Column("CompetitionId");
Table("CompetitionAnswers");
}
Voici un exemple de code que j'ai utilisé pour tester ce scénario, qui génère l'erreur:
Competition c = new Competition();
c.Description = "Description";
c.Title = "Title";
CompetitionAnswer a1 = new CompetitionAnswer { Answer = "Answer 1" };
CompetitionAnswer a2 = new CompetitionAnswer { Answer = "Answer 2" };
CompetitionAnswer a3 = new CompetitionAnswer { Answer = "Answer 3" };
c.CompetitionAnswers.Add(a1);
c.CompetitionAnswers.Add(a2);
c.CompetitionAnswers.Add(a3);
session.Save(c);
L'erreur exacte que je reçois dès qu'il essaie de Sauver de l'est:
Impossible d'insérer la valeur NULL dans la
la colonne "CompetitionId", table
'CompetitionAnswers'; la colonne n'est pas
autoriser les valeurs null. INSERTION échoue. L'
la déclaration a été résilié.
Quelqu'un peut s'il vous plaît faire toute la lumière sur pourquoi ce n'est pas actuellement en train de travailler?
OriginalL'auteur marcusstarnes | 2011-03-08
Vous devez vous connecter pour publier un commentaire.
Je suis assez sûr, n'est pas à 100%, que le problème est l'Inverse() de la spécification de votre cartographie de CompetitionAnswers sur la Concurrence. Inverse() indique que les enregistrements de l'enfant sont responsables de la définition de leur relation à la mère. Le plus souvent, le côté "un" de un un-à-plusieurs (la mère) est le "haut" de la un objet graphique et le "propriétaire" de la relation avec ses enfants. Parents qui ont des enfants, et la décision quant à l'opportunité de maintenir ou de donner l'enfant en adoption, les parents. Cependant, ce n'est pas toujours le cas; un collège, les élèves, mais ce sont les étudiants qui ont le réel pouvoir de décider où ils vont aller. Ici, l'Élève est le "top" du graphe, et que l'École est seulement un monolithique enregistrement de l'identification de l'Élève de la fréquentation. L'Étudiant peut transférer à tout moment; c'est leur décision, et il ne change pas vraiment l'École de manière significative, de sorte que les Étudiants sont responsables d'identifier eux-mêmes comme appartenant à l'École.
Votre cas est une première: les Compétitions ont CompetitionAnswers, et l'enfant n'est pas logiquement avons la responsabilité de dire: "j'appartiens à une Concurrence"; la Concurrence au lieu de "propriétaire" de sa collection de réponses. Retrait de l'Inverse() l'instruction doit rendre NH traiter de la Concurrence comme le "top" de l'objet graphique, donc NH va insérer la Compétition, le CompetitionAnswers, qui peut désormais référence à leur parent de l'ID.
Une autre chose à ne pas en rapport avec le problème, mais si vous êtes à la cartographie à un Serveur MS SQL base de données, et la colonne ID est défini comme une colonne d'identité dans la DB, j'aimerais spécifier
GeneratedBy.Identity()
pour l'ID de colonnes.Native()
DEVRAIT se terminer à l'aide d'Identité, mais il va également vérifier pour voir si HiLo ou d'une Séquence de méthodes sont disponibles.OriginalL'auteur KeithS
et aussi assurez-vous que l'enfant indique le parent en plus de la holding mère à l'enfant dans sa collection. Ceci est nécessaire lorsque le parent est l'inverse.
Était que la colonne
non null
sur la base de données, mais mappé comme nullable sur NH côté?.Not.KeyNullable()
, mais cette réponse a aidé donc +1bref... concis... fonctionne correctement. Merci pour ce!
OriginalL'auteur dotjoe
Il n'y a rien de mal avec votre mappages. Le problème est probablement avec votre base de données. Si vous utilisez les colonnes d'Identité pour vos clés primaires, les CompetitionId colonne doit être configuré pour accepter les valeurs null. La façon NHibernate fonctionne lorsque vous disposez d'un tout nouvel objet avec les enfants qui est enregistré, est-il insère l'objet parent et l'enfant des objets. Puis il met à jour l'objet enfant les clés étrangères avec le nouveau parent id de l'objet.
J'ai trouvé ce NHibernate en cascade enregistrer
en fait non, il ne le fait pas. Il génère l'ID d'abord, il sauvera les enfants (en cascade), puis il enregistre l'objet. Si l'id ne peut pas être généré jusqu'à ce que après un insert (eg. colonne d'identité sur MS SQL server), puis il vous fera économiser de l'enfant avec une valeur null pour la clé étrangère, puis de les mettre à jour avec la bonne valeur après le parent est inséré.
Pas si la table utilise une colonne d'identité, ce qui est vrai quand il est mappé à l'aide d'GeneratedBy.Natif() (comme il est dans l'OP) et la base de données prend en charge les colonnes d'identité. La DB produit de toutes les valeurs ID sauf dans certains cas de Guid.
Je suis juste allé à travers le code, et vous avez tort. Voir
IdentityGenerator
, si vous notez qu'il met en œuvreIPostInsertIdentifierGenerator
. Si vous cochezAbstractSaveEnventListener.SaveWithGeneratedId
quand il y a un PostInsert génératrice, il spécifie la valeur null pour l'id de l'argument de laPerformSave
méthode, qui à son tour indique la valeur null pour la clé. RegardezPerformSaveOrReplicate
. Si vous notez qu'il définit l'id de la valeur null, alors il le fait d'uneCascadeBeforeSave
, suivie par une insertion, suivi par unCascadeAfterSave
J'ai essayé en permettant à des valeurs NULL pour les CompetitionId champ dans la CompetitionAnswers table plus tôt, et alors qu'il n'a pas d'erreur, et le 3 CompetitionAnswer enregistrements ont été écrites, pas de CompetitionId a été inclus avec ces enregistrements, ils sont restés NULL dans la (SQL Server 2008) DB. Je suis sûr que j'ai essayé de supprimer l'Inverse() et il produit la même erreur que j'ai collé dans l'OP, même si je vais devoir essayer de nouveau pour assurez-vous que lorsque je suis de retour au travail le matin.
OriginalL'auteur Vadim