2009-03-03 9 views
5

¿Cómo se correlaciona una clase con otras instancias de la misma clase cuando esa relación tiene propiedades en sí?Asignaciones de NHibernate cuando las relaciones de unión automática tienen propiedades adicionales

tengo una clase llamada Persona que se asigna a una persona mesa

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

Quiero una relación de muchos a muchos entre persona y persona usando una tabla de unión llamado PersonPerson:

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

quiero los siguientes atributos en la tabla PersonPerson:

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

This question y el enlace a post by Billy McCafferty explica que la relación PersonPerson debe promoverse desde una JOIN normal a una entidad en sí misma debido a las columnas adicionales en la tabla PersonPerson. Sin embargo, no explica a qué cuándo es una auto-unión. La diferencia es que si pregunto por todas las personas relacionadas a Dave Dee (ID = 1), no solo debería obtener Tich (ID = 5), sino que también debería obtener Dozy (ID = 2) también porque Dave Dee también está en la columna RelatedPersonID.

Lo que mi solución es hasta ahora, es tener dos propiedades en mi clase 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; 
    } 
} 

y tienen el siguiente en el 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> 

Esto parece un poco torpe y poco elegante. NHibernate generalmente tiene soluciones elegantes para la mayoría de los problemas cotidianos. ¿Es lo anterior la forma sensata de hacerlo o hay una mejor manera?

Respuesta

2

Creo que también lo haría así, pero creo que es un poco "torpe" modelarlo así. Es decir: tiene una colección de personas con las que está relacionada una determinada persona, pero también tiene una "relación inversa".
¿Es esto realmente necesario? ¿No es una opción eliminar esta colección secundaria y, en su lugar, especificar un método en el repositorio de personas que pueda devolverle a todas las personas que tienen algún tipo de relación con una persona determinada?

Hmm, esto puede sonar un poco oscuro, así que aquí hay un código (nótese que en aras de la brevedad, dejé los modificadores 'virtuales', etc ... (También prefiero no tener esos modificadores) , por lo que en el 99% de las veces, especifico 'lazy = false' en mi clase de mapeo).

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 se puede ver, la clase de persona sólo tiene una colección de la izquierda, que es una colección de PersonPerson objetos que representan las relaciones que tiene esta Persona. Para obtener las Personas que tienen relaciones con una Persona determinada, puede crear un método específico en su Depósito Personal que devuelva esas Personas, en lugar de tenerlas en una colección en la clase Persona. creo esto mejorará el rendimiento también.

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

    } 
} 

La 'referencia posterior' no es un miembro de la clase Persona; se debe acceder a través del repositorio. Esto es también lo que dice Eric Evans en su libro DDD: en algunos casos, es mejor tener un método especializado en el repositorio que pueda darle acceso a los objetos relacionados, en lugar de tenerlos (= los objetos relacionados) para llevar alrededor con el objeto mismo.

No probé el código, lo escribí aquí, así que tampoco compruebo el error de sintaxis, etc ... pero creo que debería aclarar un poco cómo lo vería.

+0

@Frederik Gheysels Gran respuesta, lo intentaré ahora. ¡Parece una solución obvia ahora que lo has dicho! –

+0

@Frederik: me gusta la idea de hacer esto en el repositorio, pero todavía no está claro cómo recuperaría todas las instancias relacionadas y los tipos de relación. –

+0

Devolvería los objetos Persona que tienen una relación con la persona dada. Por supuesto, esos objetos Person tienen su colección 'PersonPerson' que contiene todas las relaciones que tiene esta Persona. –

2

A mi me parece como que ha construido un modelo esencialmente de un directed graph, y las dos asignaciones PersonPersonForward y PersonPersonBack representan bordes salientes y entrantes, respectivamente.

Esta direccionalidad se ve reforzada por la semántica de los tipos de relaciones: es más probable mientras is-a-colega de un symmetric relation, is-a-Gerente de y is-a-tutor de son casi definitivamente asimétricos.

Creo que en este caso el modelo de datos intenta decirle que las dos colecciones de enlaces, aunque de tipo compatible, no son lo mismo en contexto.

+0

Estas son tablas ligeramente artificiales, pero entiendo su punto. Me gustaría que las relaciones simétricas se trataran de la misma manera que las asimétricas. ¿No significaría eso que los simétricos son los mismos en cada sentido? –

+0

Sí, un borde no dirigido en un gráfico dirigido de otro modo estaría representado por un par de bordes dirigidos, uno apuntando en cada dirección. –

+0

Si realmente busca un gráfico híbrido y semidirigido, intente separar las relaciones simétricas en su propia tabla con una restricción de comprobación (ID izquierda <= ID-derecha) para la normalización, y tal vez una vista con mutadores apropiados definidos para expresar la simetría relacionalmente. –

Cuestiones relacionadas