2009-03-03 14 views
5

Come si associa una classe ad altre istanze della stessa classe quando tale relazione ha le proprietà stesse?Mapping NHibernate quando le relazioni self-join hanno proprietà aggiuntive

ho una classe denominata persona che è associato a una persona tavolo

PersonID PersonName PersonAge 
---------------------------------- 
     1 Dave Dee    55 
     2 Dozy     52 
     3 Beaky    45 
     4 Mick     55 
     5 Tich     58 

Voglio una relazione molti-a-molti tra persona e persona utilizzando una tabella di join chiamato PersonPerson:

PersonPersonID PersonID RelatedPersonID RelationshipID 
-------------------------------------------------------- 
       1   1    5    1 
       2   3    4    2 
       3   2    1    3 

voglio i seguenti attributi nella tabella PersonPerson:

RelationshipID RelationshipName 
-------------------------------- 
      1 Colleague 
      2 Manager 
      3 Tutor 

This question e il collegamento a post by Billy McCafferty spiegano che la relazione PersonPerson deve essere promossa da un normale JOIN a un'entità a sé stante a causa delle colonne aggiuntive nella tabella PersonPerson. Tuttavia non spiega cosa fare quando si tratta di un self-join. La differenza è che se chiedo tutte le persone correlate a Dave Dee (ID = 1), non solo dovrei ottenere Tich (ID = 5), ma dovrei ottenere anche Dozy (ID = 2 anche perché Dave Dee si trova anche nella colonna RelatedPersonID.

Ciò che la mia soluzione è finora, è di avere due proprietà nella mia classe Person.

public virtual IList<PersonPerson> PersonPersonForward {get;set;} 
public virtual IList<PersonPerson> PersonPersonBack {get;set;} 

private List<PersonPerson> personPersonAll; 
public virtual List<PersonPerson> PersonPersonAll 
{ 
    get 
    { 
     personPersonAll = new List<PersonPerson>(PersonPersonForward); 
     personPersonAll.AddRange(PersonPersonBack); 
     return personPersonAll; 
    } 
} 

E hanno il seguente nella HBM:

<bag name="PersonPersonForward" table="PersonPerson" cascade="all"> 
     <key column="PersonID"/> 
     <one-to-many class="PersonPerson" /> 
</bag> 

<bag name="PersonPersonBack" table="PersonPerson" cascade="all"> 
     <key column="RelatedPersonID"/> 
     <one-to-many class="PersonPerson" /> 
</bag> 

questo sembra un po 'goffo e poco elegante. NHibernate di solito ha soluzioni eleganti per la maggior parte dei problemi quotidiani. Quanto sopra è il modo ragionevole di farlo o c'è un modo migliore?

risposta

2

Penso che lo farei anch'io, ma, penso che sia un po 'goffo modellarlo in questo modo. Voglio dire: hai una collezione di persone a cui una certa persona è correlata, ma hai anche una "back-relation".
È davvero necessario? Non è un'opzione per rimuovere questo back-collection e invece, specificare un metodo sul PersonRepository che può restituire a tutte le persone che hanno qualche tipo di relazione con una determinata persona?

Hmm, questo può sembrare un po 'oscuro, quindi ecco un po' di codice , così nel 99% del tempo, a specificare 'pigro = false' a mia classe-mapping).

public class Person 
{ 
    public int Id {get; set;} 
    public string Name {get; set;} 

    public IList<PersonPerson> _relatedPersons; 

    public ReadOnlyCollection<PersonPerson> RelatedPersons 
    { 
     get 
     { 
      // The RelatedPersons property is mapped with NHibernate, but 
      // using its backed field _relatedPersons (can be done using the 
      // access attrib in the HBM. 
      // I prefer to expose the collection itself as a readonlycollection 
      // to the client, so that RelatedPersons have to be added through 
      // the AddRelatedPerson method (and removed via a RemoveRelatedPerson method). 

      return new List<PersonPerson) (_relatedPersons).AsReadOnly(); 
     } 
    } 

    public void AddRelatedPerson(Person p, RelationType relatesAs) 
    { 
     ... 
    } 

} 

Come si può vedere, la classe Person solo ha una collezione di sinistra, che è una raccolta di PersonPerson oggetti che rappresentano i rapporti che questa persona ha. Al fine di ottenere le persone che hanno relazioni con una determinata persona, è possibile creare un metodo specifico sul proprio PersonRepository che restituisce tali persone, invece di averle in una raccolta nella classe Person. credo questo migliorerà anche le prestazioni.

public class NHPersonRepository : IPersonRepository 
{ 
    ... 

    public IList<Person> FindPersonsThatHaveARelationShipWithPerson(Person p) 
    { 
     ICriteria crit = _session.CreateCriteria <Person>(); 

     crit.AddAlias ("RelatedPersons", "r"); 

     crit.Add (Expression.Eq ("r.RelatedWithPerson", p)); 

     return crit.List(); 

    } 
} 

Il "riferimento indietro" non è un membro della classe Persona; deve essere accessibile tramite il repository. Questo è anche ciò che dice Eric Evans nel suo libro DDD: in alcuni casi, è meglio avere un metodo specializzato nel repository che può dare accesso agli oggetti correlati, invece di averli (= gli oggetti correlati) da portare in giro con l'oggetto stesso.

Non ho testato il codice, l'ho appena inserito qui, quindi non ho verificato errori di sintassi, ecc ... ma penso che dovrebbe chiarire un po 'come lo vedrei.

+0

@Frederik Gheysels Ottima risposta, ci proverò ora. Sembra una soluzione ovvia ora che l'hai detto! –

+0

@Frederik: mi piace l'idea di farlo nel repository, ma non mi è ancora chiaro come recuperare tutte le istanze correlate e i tipi di relazione. –

+0

Vorrei restituire gli oggetti Persona che hanno una relazione con la persona data. Offcourse, quegli oggetti Person hanno la loro collezione 'PersonPerson' che contiene tutte le relazioni che questa persona ha. –

2

Mi sembra che tu abbia essenzialmente realizzato un modello di directed graph e che i due mapping PersonPersonForward e PersonPersonBack rappresentino rispettivamente i bordi in uscita e quelli in entrata.

Questa direzionalità è rafforzata dalla semantica dei vostri tipi di rapporto: mentre è-un-collega-of è più probabile un symmetric relation, is-a-Manager di e is-a-Tutor-of sono quasi sicuramente asimmetrici.

Penso che in questo caso il modello di dati sta cercando di dirti che le due raccolte di collegamenti, mentre di tipo compatibile, non sono la stessa cosa nel contesto.

+0

Queste sono tabelle un po 'artificiose, ma prendo nota del punto. Vorrei che le relazioni simmetriche fossero trattate come quelle asimmetriche. Non significherebbe solo che quelli simmetrici sono uguali in ogni modo? –

+0

Sì, un lato non orientato in un grafico indirizzato diversamente sarebbe rappresentato da una coppia di bordi diretti, uno rivolto verso ciascun senso. –

+0

Se stai davvero seguendo un grafico ibrido, semi-diretto, prova a separare le relazioni simmetriche nella loro tabella con un vincolo di controllo (left-id <= right-id) per la normalizzazione, e forse una vista con i mutatori appropriati definiti per esprimere la simmetria in relazione. –

Problemi correlati