NHibernate сопоставления, когда отношения самосоединения обладают дополнительными свойствами
-
03-07-2019 - |
Вопрос
Как вы сопоставляете класс с другими экземплярами того же класса когда это отношение само по себе обладает свойствами?
У меня есть класс под названием 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 должно быть переведено из обычного ОБЪЕДИНЕНИЯ в самостоятельную сущность из-за дополнительных столбцов в таблице PersonPerson.Однако это не объясняет, что делать, когда это самосоединение.Разница в том, что если я попрошу всех связанных с этим людей Дэйв Ди (ID = 1), я не только должен получить Тич (ID = 5), но я должен получить также get Сонный (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;
}
}
И иметь следующее в 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>
Это кажется немного неуклюжим и неэлегантным.NHibernate обычно предлагает элегантные решения большинства повседневных проблем.Является ли приведенное выше разумным способом сделать это или есть способ получше?
Решение
Я думаю, что я бы тоже сделал это таким образом, но, по-моему, это немного "неуклюже" моделировать это таким образом.Я имею в виду:у вас есть набор людей, с которыми связан определенный человек, но у вас также есть "обратная связь".
Действительно ли это необходимо ?Разве это не вариант удалить эту обратную коллекцию и вместо этого указать метод в 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 - книге:в некоторых случаях лучше иметь специализированный метод в репозитории, который может предоставить вам доступ к связанным объектам, вместо того, чтобы переносить их (= связанные объекты) с самим объектом.
Я не тестировал код, я просто ввел его здесь, поэтому я также не проверял наличие синтаксической ошибки и т.д...но я думаю, это должно немного прояснить, как бы я на это посмотрел.
Другие советы
Мне кажется, что вы, по сути, построили модель ориентированный граф, и два сопоставления PersonPersonForward
и PersonPersonBack
представляют собой исходящие и входящие ребра соответственно.
Эта направленность усиливается семантикой ваших типов отношений:в то время как является-Коллегой-из скорее всего, это симметричное отношение, является менеджером и является-наставником- почти определенно асимметричны.
Я думаю, что в данном случае модель данных пытается сказать вам, что две коллекции ссылок, хотя и имеют совместимый тип, не являются одним и тем же в контексте.