2009-03-13 12 views
22

Ogni volta che carico una classe Task, la proprietà Document è sempre nullo, nonostante siano presenti dati nel db.fluente nibernato HasOne WithForeignKey non funziona

classe

Task:

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

Corrispondenza attività override per AutoPersistenceModel:

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

Come si può vedere formano ciò che NHProf dice è in esecuzione, la condizione di join è sbagliato, per la squadra di WithForeignKey sembrano prende effetto. In effetti, posso scrivere qualsiasi stringa nel codice sopra e non fa differenza.

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

Dovrebbe essere:

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

Se i hackero i dati nel DB in modo che gli ID corrispondono, i dati vengono caricati, ma ovviamente questo non è corretto - ma almeno dimostra carica dati.

Edit: frugando nella fonte nhib fluente per trovare l'XML produce questo:

<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: Ecco lo schema:

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, 

Chiunque ha ottenuto tutte le idee?

Grazie

Andrew

risposta

5

Credo che il problema qui è che la convenzione "HasOne" significa che si punta a l'altra cosa (il modo relazionale standard per dire "molti a uno"/"One a uno "); Inserendo un Task_ID sul documento, la relazione effettiva è una HasMany ma si ha una comprensione implicita che ci sarà un solo documento per attività.

Scusate - Non so come risolvere questo problema, ma sarei interessato a vedere quale sia la soluzione (non uso NHibernate o Fluent NHibernate, ma ho cercato di utilizzarlo in futuro) . Una soluzione (da qualcuno con pochissima idea) sarebbe quella di rendere Documents una raccolta su Task e quindi fornire una proprietà Document che restituisce la prima nella raccolta (utilizzando un'interfaccia che nasconde la proprietà Documents in modo che nessuno pensi di poter aggiungere nuovi elementi ad esso).

Guardando attraverso la documentazione e considerando la risposta di eulerfx, forse l'approccio sarebbe qualcosa di simile:

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

EDIT: Proprio così questa risposta ha la soluzione appropriata: Il percorso corretto è quello di aggiornare lo schema del database che corrisponda al intento del codice. Ciò significa aggiungere un DocumentID alla tabella delle attività, in modo che esista una relazione molti a uno tra l'attività e il documento. Se le modifiche allo schema non fossero possibili, References() sarebbe la risoluzione appropriata.

+0

A possibilità sono d'accordo, ma perché Task_Id non viene visualizzato da nessuna parte? (anche se è sbagliato e quindi causerebbe un'eccezione - ma non lo è) –

+0

Che funziona, tuttavia significa che ho bisogno di una proprietà Task_id su Documento, che preferirei non avere. Funziona per ora però grazie –

6

È necessario utilizzare:

Riferimenti (x => x.Documento, "DocumentIdColumnOnTask")

+0

tblDocument ha Task_Id, non tblTask ​​ha Document_Id –

+0

Oh ok perso quella parte. Quindi suppongo che sarebbe più sensato avere un docID sulla tabella delle attività. In caso contrario, la struttura della tabella indica che potrebbero essere disponibili più documenti per un'attività. – eulerfx

+0

Sì, suppongo di sì. C'era una ragione è come è ma non riesco a ricordare perché. Devo pensare e prendere in considerazione la possibilità di capovolgerlo. ta –

0

Come eulerfx sottolineato,

la struttura della tabella indica che là forse mulitple documenti per un compito

e Chris ha dichiarato:

By inserendo un ID task sul documento, la relazione effettiva è HasMany ma si ha una sorta di comprensione implicita che ci sarà un solo documento per attività.

Questo è ovviamente corretto quindi l'ho invertito in modo che Task abbia un ID documento non valido.

Grazie a tutti e due per il vostro aiuto!

Ho lanciato una moneta per la risposta accettata, se avessi potuto spuntare entrambe le sarei!

+0

hey andrew, PLN's in defo la risposta. Direi di rifare il db in realtà hai fatto la cosa giusta. Ma a volte non è possibile modificare il DB, ed è qui che i Fluent Mappings brillano davvero. HasOne è una di queste funzionalità, e sia tu che io stavamo usando in modo errato. PLN indica il modo giusto di usarlo ... i miei 2 centesimi – andy

91

Ho incontrato lo stesso problema oggi. Credo che il trucco non sia usare .ForeignKey (...) con il mapping .HasOne, ma usare .PropertyRef (...). Quello che segue è come mi definisco uno-a-uno tra un'organizzazione (Capogruppo) e la sua amministrazione (Bambino):

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

l'amministratore ha un semplice riferimento all'Organizzazione utilizzando la sua chiave esterna:

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

Quando si recupera un'organizzazione, viene caricato il record di amministrazione corretto e vengono eseguiti correttamente gli aggiornamenti e le eliminazioni.

+0

Sembra che la risposta perfetta sarebbe una combinazione di questo e la risposta di Chris Shaffer. Questa risposta fornisce un'implementazione funzionante con Fluent NHibernate e Chris Shaffer fornisce un po 'di "teoria" sul perché l'implementazione iniziale non funzionava. –

+0

Grazie ... ha funzionato per me – dbones

+0

ha funzionato anche per me, grazie mille. Ma cos'è HasOne(). ForeignKey() per? – whitestream

0

Ho lottato con lo stesso ha un problema e finalmente trovato che questo ha funzionato:

public class ParentMap : ClassMap<Parent> 
{ 
    public ParentMap() 
    { 
     Id(x => x.Id); 
     HasOne(s => s.Child).Cascade.All(); 
    } 
} 

public class ChildMap : ClassMap<Model.Child> 
{ 
    public ChildMap() 
    { 
     Id(x => x.Id); 
     HasOne(s => s.Parent).Constrained().ForeignKey();   
    } 
} 
+0

come fare con compositeid in entrambe le tabelle? – DanielVorph

2

ho provato questa soluzione:

solo nel documento:

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