Mapeamentos de Nibernato quando os relacionamentos de auto-joia têm propriedades adicionais

StackOverflow https://stackoverflow.com/questions/606330

Pergunta

Como você mapeia uma classe para outras instâncias da mesma classe Quando esse relacionamento tem propriedades em si?

Eu tenho uma classe chamada que é mapeada para uma pessoa de mesa

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

Eu quero um relacionamento de muitos para muitos entre pessoa e pessoa usando uma tabela de junção chamada PersonPerson:

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

Eu quero os seguintes atributos na tabela Personperson:

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

Essa questão e o vinculado a Postado por Billy McCafferty Explica que o relacionamento pessoal deve ser promovido de uma junção normal a uma entidade por si só por causa das colunas adicionais na tabela de pessoas. No entanto, não explica o que é quando é uma auto-joia. A diferença é que, se eu pedir todas as pessoas relacionadas a Dave Dee (Id = 1), não apenas devo conseguir Tich (Id = 5), mas eu também deveria obter Dozy (Id = 2) também porque Dave Dee também está na coluna RelatedPersonID.

Qual é a minha solução até agora é ter duas propriedades na minha aula de pessoa.

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 tenha o seguinte no 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>

Isso parece um pouco desajeitado e deselegante. O Nibernate geralmente tem soluções elegantes para a maioria dos problemas cotidianos. A maneira acima é a maneira sensata de fazer isso ou existe uma maneira melhor?

Foi útil?

Solução

Eu acho que faria assim também, mas acho um pouco 'desajeitado' modelá -lo assim. Quero dizer: você tem uma coleção de pessoas às quais uma certa pessoa está relacionada, mas também tem uma 'relação de volta'.
Isso é realmente necessário? Não é uma opção remover essa coleta traseira e, em vez disso, especifique um método no Pessoal Reprepository que pode devolver todas as pessoas que têm algum tipo de relação com uma determinada pessoa?

Hmm, isso pode parecer um pouco obscuro, então aqui está algum código (observe que, por uma questão de brevidade, deixei de fora os modificadores 'virtuais' etc ... (eu também prefiro não ter esses modificadores, então em 99 % das vezes, especifiquei 'preguiçoso = falso' no meu mapeamento de 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 )
    {
       ...
    }

}

Como você pode ver, a classe Pessoa só tem uma coleção, que é uma coleção de objetos de pessoa que representa as relações que essa pessoa tem. Para obter as pessoas que tenham relações com uma determinada pessoa, você pode criar um método específico em seu reembolso pessoal que devolve essas pessoas, em vez de tê -las em uma coleção na classe Pessoa. Eu acho que isso também melhorará o desempenho.

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();

    }
}

A 'referência de volta' não é um membro da classe da pessoa; Ele deve ser acessado através do repositório. É também o que Eric Evans diz em seu DDD - Livro: Em alguns casos, é melhor ter um método especializado no repositório que possa lhe dar acesso a objetos relacionados, em vez de tê -los (= os objetos relacionados) para transportar ao redor com o próprio objeto.

Eu não testei o código, apenas digitei aqui, então também não verifiquei o erro de sintaxe, etc ... mas acho que deveria esclarecer um pouco sobre como eu veria isso.

Outras dicas

Parece -me que você essencialmente construiu um modelo de um Gráfico direcionado, e os dois mapeamentos PersonPersonForward e PersonPersonBack Representar arestas de saída e entrada, respectivamente.

Essa direção é reforçada pela semântica de seus tipos de relacionamento: enquanto é um colega-de-liga provavelmente é um relação simétrica, Is-a-manager-de e Is-a-tutor-of são quase definitivamente assimétricos.

Eu acho que, neste caso, o modelo de dados está tentando dizer que as duas coleções de links, enquanto do tipo compatível, não são a mesma coisa no contexto.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top