Отображения NHibernate, когда отношения самостоятельного соединения имеют дополнительные свойства - PullRequest
5 голосов
/ 03 марта 2009

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

У меня есть класс с именем 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

Этот вопрос и связанная с публикация Билли МакКафферти объясняют, что отношение PersonPerson должно быть преобразовано из обычного JOIN в отдельную сущность из-за дополнительного столбцы в таблице PersonPerson. Однако это не объясняет, что делать, когда это самостоятельное соединение. Разница в том, что если я попрошу всех связанных с этим людей Дейва Ди (ID = 1), я не только получу Tich (ID = 5), но я также должен получить get Dozy (ID = 2), потому что Дейв Ди также находится в столбце 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 ]

2 голосов
/ 06 марта 2009

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

Эта направленность усиливается семантикой ваших типов отношений: тогда как is-a-Colleague-of , скорее всего, является симметричным отношением , is-a-Manager -of и is-a-Tutor-of почти определенно асимметричны.

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

2 голосов
/ 06 марта 2009

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

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

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. Чтобы получить людей, имеющих отношения с данным человеком, вы можете создать специальный метод в вашем PersonRepository, который возвращает эти люди, вместо того, чтобы помещать их в коллекцию класса 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: в некоторых случаях лучше иметь специализированный метод для хранилища, который может дать вам доступ к связанным объектам, вместо того, чтобы иметь их (= связанные объекты) для переноски. с самим объектом.

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

...