2009-03-03 2 views
5

Как вы сопоставляете класс с другими экземплярами того же класса , когда это отношение имеет собственные свойства?Отображения NHibernate, когда отношения с самостоятельным объединением имеют дополнительные свойства

У меня есть класс с именем Person, который преобразуется в таблицу Person

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

Я хочу много-ко-многим между человеком и человеком с помощью таблицы объединения под названием PersonPerson:

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

Я хочу, чтобы следующие атрибуты в PersonPerson таблице:

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

This question, а связанный с post by Billy McCafferty поясняет, что отношения PersonPerson должны продвигаться от обычного JOIN к объекту в своем собственном праве из-за дополнительных столбцов в таблице PersonPerson. Однако это не объясняет, к чему, когда это самосоединение. Разница в том, что если я попрошу всех связанных людей до Dave Dee (ID = 1), я должен получить не только Tich (ID = 5), но я также должен получить Dozy (ID = 2), так как Dave Dee также находится в столбце RelatedPersonID.

До сих пор мое решение состоит в том, чтобы иметь два свойства в моем классе 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; 
    } 
} 

И имеют следующее НВМ:

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

Это кажется мелочью неуклюжим и безвкусный. NHibernate обычно имеет элегантные решения для большинства повседневных проблем. Является ли выше разумный способ сделать это или есть лучший способ?

ответ

2

Я думаю, что я сделал бы это так же, но, я думаю, что это немного «неуклюжий», чтобы моделировать это так. Я имею в виду: у вас есть коллекция людей, с которыми связано определенное лицо, но у вас также есть «обратная связь».
Действительно ли это необходимо? Разве это не вариант удаления этой обратной коллекции, а вместо этого укажите метод в PersonRepository, который может дать вам всех лиц, которые имеют какие-то отношения с данным человеком?

Хм, это может показаться немного неясным, так что вот какой-то код (обратите внимание, что для краткости я оставил «виртуальные» модификаторы и т. Д. (Я также предпочитаю не иметь этих модификаторов , так что в 99% времени, я указываю «ленивым = ложь» в моем классе картирования).

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) 
    { 
     ... 
    } 

} 

Как вы можете видеть, класс Person только имеет один сборник слева, то есть коллекция PersonPerson объекты, которые представляют отношения, которые у этого Лица есть. Чтобы получить Лица, имеющие отношения с данным Лицом, вы можете создать конкретный метод в вашем Личном Репозитории, который возвращает этих Лица, вместо того, чтобы иметь их в коллекции класса Person. я думаю это также улучшит производительность.

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

    } 
} 

«обратная ссылка» не является членом класса Person; он должен быть доступен через репозиторий. Это также говорит Эрик Эванс в своей книге DDD: в некоторых случаях лучше иметь специализированный метод в репозитории, который может дать вам доступ к связанным объектам, вместо того чтобы иметь их (= связанные объекты) для переноса вокруг с самим объектом.

Я не тестировал код, я просто набрал его здесь, поэтому я также не проверял ошибку синтаксиса и т. Д., Но я думаю, что он должен немного разъяснить, как я это увижу.

+0

@Frederik Gheysels Отличный ответ, я попробую это сейчас. Теперь кажется очевидным решением, что вы это сказали! –

+0

@Frederik - Мне нравится идея сделать это в репозитории, но мне все еще неясно, как я верну все связанные экземпляры и типы отношений. –

+0

Я бы вернул объекты Person, которые имеют отношения с данным человеком. Итак, эти объекты Person имеют свою коллекцию PersonPerson, которая содержит все отношения, которые имеет этот Человек. –

2

Это похоже на то, что вы по существу построили модель directed graph, а два сопоставления PersonPersonForward и PersonPersonBack представляют собой исходящие и входящие грани соответственно.

Это Направленность подкрепляется семантикой ваших типов отношений: в то время как это-Коллега-из, скорее всего, symmetric relation, это-менеджер-из и это-Tutor-из почти определенно асимметричны.

Я думаю, что в этом случае модель данных пытается сказать вам, что две коллекции ссылок, в то время как совместимый тип, не являются одним и тем же в контексте.

+0

Это немного надуманные таблицы, но я беру вашу мысль. Я бы хотел, чтобы симметричные отношения обрабатывались так же, как и асимметричные. Разве это не означает, что симметричные одни одинаковы? –

+0

Да, неориентированный край в противном случае направленный граф будет представлен парой направленных ребер, каждый из которых указывает каждый путь. –

+0

Если вы действительно после гибридного полуограниченного графа, попробуйте отделить симметричные отношения в своей таблице с контрольным ограничением (left-id <= right-id) для нормализации и, возможно, с соответствующими мутаторами, определенными для выражают симметрию реляционно. –