23 votes

fluent nhibernate HasOne WithForeignKey ne fonctionne pas

Lorsque je charge une classe Task, la propriété Document est toujours nulle, bien qu'il y ait des données dans la base de données.

Classe de tâches :

public class Task
{
    public virtual Document Document { get; set; }

Surcharge de la cartographie des tâches pour AutoPersistenceModel :

public void Override(AutoMap<Task> mapping)
{
    mapping.HasOne(x => x.Document)
        .WithForeignKey("Task_Id");

Comme vous pouvez le voir dans ce que NHProf dit être exécuté, la condition de jointure est erronée, le WithForeignKey ne semble pas avoir d'effet. En fait, je peux écrire n'importe quelle chaîne dans le code ci-dessus et cela ne fait aucune différence.

FROM   [Task] this_
    left outer join [Document] document2_
    on this_.Id = document2_.Id

Il devrait l'être :

FROM   [Task] this_
    left outer join [Document] document2_
    on this_.Id = document2_.Task_Id

Si je pirate les données dans la base de données pour que les identifiants correspondent, les données sont chargées, mais c'est évidemment incorrect - mais au moins cela prouve que les données sont chargées.

Edit : en fouillant dans la source de fluent nhib pour trouver le XML, on obtient ceci :

<one-to-one foreign-key="Task_Id" cascade="all" name="Document" class="MyProject.Document, MyProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> 

Edit : voici le schéma :

CREATE TABLE [dbo].[Document](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Task_Id] [int] NOT NULL,

CREATE TABLE [dbo].[Task](
[Id] [int] IDENTITY(1,1) NOT NULL,

Quelqu'un a-t-il une idée ?

Remerciements

Andrew

94voto

PLN Points 861

J'ai rencontré le même problème aujourd'hui. Je pense que l'astuce consiste à ne pas utiliser .ForeignKey(...) avec le mappage .HasOne, mais à utiliser .PropertyRef(...) à la place. Voici comment je définis une relation univoque entre une organisation (parent) et son administrateur (enfant) :

HasOne(x => x.Admin).PropertyRef(r => r.Organisation).Cascade.All();

L'administrateur a une référence simple à l'organisation en utilisant sa clé étrangère :

References(x => x.Organisation, "ORAD_FK_ORGANISATION").Not.Nullable();

Lors de la récupération d'une organisation, l'enregistrement administratif correct est chargé et les mises à jour et suppressions sont effectuées en cascade.

6voto

eulerfx Points 16320

Vous devez utiliser :

References(x => x.Document, "DocumentIdColumnOnTask")

5voto

Chris Shaffer Points 18066

Je pense que le problème ici est que la convention "HasOne" signifie que vous pointez vers l'autre chose (la façon relationnelle standard de dire "Many To One"/"One to One") ; En mettant un Task_ID sur le document, la relation réelle est un HasMany mais vous avez une sorte de compréhension implicite qu'il n'y aura qu'un seul document par tâche.

Désolé - je ne sais pas comment résoudre ce problème, mais je serai intéressé de voir quelle est la solution (je n'utilise pas NHibernate ou Fluent NHibernate, mais j'ai fait des recherches pour l'utiliser à l'avenir). Une solution (de la part de quelqu'un qui a très peu d'idées) serait de faire de Documents une collection sur Task, et de fournir une propriété Document qui renvoie le premier de la collection (en utilisant une interface qui cache la propriété Documents pour que personne ne pense qu'il peut y ajouter de nouveaux éléments).

En regardant la documentation et en tenant compte de la réponse d'eulerfx, il se peut que l'approche soit quelque chose comme :

References(x => x.Document)
    .TheColumnNameIs("ID")
    .PropertyRef(d => d.Task_ID);

EDIT : Juste pour que cette réponse ait la solution appropriée : La bonne solution consiste à mettre à jour le schéma de la base de données pour qu'il corresponde à l'intention du code. Cela signifie qu'il faut ajouter un DocumentID à la table Task, afin qu'il y ait une relation Many-To-One entre Task et Document. S'il n'était pas possible de modifier le schéma, References() serait la solution appropriée.

2voto

tommy Points 41

J'ai essayé cette solution :

juste dans le document :

mapping.HasOne(x => x.Task).ForeignKey("Task_ID").Constrained().Cascade.All();

0voto

Andrew Bullock Points 14899

Comme l'a souligné eulerfx,

la structure du tableau indique qu'il peut y avoir plusieurs documents pour une même tâche

et Chris a déclaré :

En mettant un identifiant de tâche sur le document, la relation réelle est une relation HasMany, mais vous avez une sorte de compréhension implicite qu'il n'y aura qu'un seul document par tâche.

C'est bien sûr correct et j'ai donc inversé les choses pour que Task ait un Document_Id nullable.

Merci à vous deux pour votre aide !

J'ai tiré à pile ou face la réponse acceptée, mais si je pouvais cocher les deux, je le ferais !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X