Asignaciones de NHibernate cuando las relaciones de unión automática tienen propiedades adicionales

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

Pregunta

¿Cómo asignas una clase a otras instancias de la misma clase cuando esa relación tiene propiedades en sí mismas ?

Tengo una clase llamada Persona que se asigna a una persona de la tabla

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 utilizando una tabla de unión llamada 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

Esta pregunta y el enlace -a post by Billy McCafferty explica que la relación PersonPerson debe promocionarse de una UNIÓN normal a una entidad por derecho propio debido a las columnas adicionales en la tabla PersonPerson. Sin embargo, no explica qué hacer cuando se trata de una auto-unión. La diferencia es que si pido a todas las personas relacionadas con Dave Dee (ID = 1), no solo debería obtener Tich (ID = 5), sino que debería obtener también obtener Dozy (ID = 2) también porque Dave Dee también se encuentra en la columna RelatedPersonID.

Lo que mi solución es hasta ahora, es tener dos propiedades en mi clase de Persona.

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 tenga lo siguiente en la 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. ¿Lo anterior es la forma sensata de hacer esto o hay una mejor manera?

¿Fue útil?

Solución

Creo que también lo haría así, pero creo que es un poco "torpe" modelarlo así. Quiero decir: tienes una colección de personas con las que una determinada persona está relacionada, pero también tienes una 'relación de retroceso'. ¿Es esto realmente necesario? ¿No es una opción eliminar esta recopilación retrospectiva 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 algo de código (tenga en cuenta que, por brevedad, dejé de lado los modificadores 'virtuales', etc. ... (también prefiero no tener esos modificadores, así que en el 99% del tiempo, especifico 'perezoso = falso' en mi asignación de clase).

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 puede ver, a la clase Persona solo le queda una colección, es decir, una colección de objetos PersonPerson que representa 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 PersonRepository que devuelva esas Personas, en lugar de tenerlas en una colección de la clase Persona. Creo que esto también mejorará el rendimiento.

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' no es un miembro de la clase Persona; Hay que acceder a través del repositorio. Esto también es lo que Eric Evans dice en su libro de DDD: en algunos casos, es mejor tener un método especializado en el repositorio que pueda darle acceso a objetos relacionados, en lugar de tenerlos (= los objetos relacionados) para llevarlos consigo con el objeto en sí.

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

Otros consejos

Me parece que esencialmente has construido un modelo de un gráfico dirigido , y las dos asignaciones PersonPersonForward y PersonPersonBack representan bordes salientes y entrantes respectivamente.

Esta orientación se ve reforzada por la semántica de sus tipos de relación: mientras que es un colega es muy probable que sea relación simétrica , is-a-Manager-of y is-a-Tutor-of son casi definitivamente asimétricos .

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top