Domanda

Come si mappa una classe ad altre istanze della stessa classe quando quella relazione ha proprietà stesse ?

Ho una classe chiamata Person che è mappata su una tabella Person

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 denominata 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

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

Qual è la mia soluzione finora, 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 avere quanto segue in 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 inelegante. NHibernate di solito offre soluzioni eleganti alla maggior parte dei problemi quotidiani. Quanto sopra è il modo sensato di farlo o esiste un modo migliore?

È stato utile?

Soluzione

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

Hmm, questo può sembrare un po 'oscuro, quindi ecco un po' di codice (nota che per brevità, ho lasciato fuori i modificatori "virtuali" ecc ... (Preferisco anche non avere quei modificatori, quindi nel 99% dei casi, specifico 'lazy = false' nella mia mappatura di classe).

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 puoi vedere, alla classe Person è rimasta solo una raccolta, ovvero una raccolta di oggetti PersonPerson che rappresenta le relazioni che questa persona ha. Al fine di ottenere le Persone che hanno relazioni con una determinata Persona, è possibile creare un metodo specifico nel proprio Repository Person che restituisca tali Persone, invece di averle in una raccolta nella classe Persona. Penso che 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 'back-reference' non è un membro della classe Person; è necessario accedervi tramite il repository. Questo è anche ciò che dice Eric Evans nel suo libro DDD: in alcuni casi, è meglio disporre di un metodo specializzato nel repository che può darti accesso ad oggetti correlati, invece di averli (= gli oggetti correlati) da portare in giro con l'oggetto stesso.

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

Altri suggerimenti

Mi sembra che tu abbia essenzialmente costruito un modello di grafico diretto , e i due mapping PersonPersonForward e PersonPersonBack rappresentano rispettivamente i bordi in uscita e in entrata.

Questa direzione è rafforzata dalla semantica dei tipi di relazioni: mentre è un collega di è molto probabilmente un relazione simmetrica , is-a-Manager-of e is-a-Tutor-of sono quasi sicuramente asimmetrici .

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top