自己結合関係に追加のプロパティがある場合の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という結合テーブルを使用して、PersonとPersonの間に多対多の関係が必要です:
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
この質問とリンク-to Billy McCaffertyによる投稿は、PersonPersonテーブルの追加列のために、PersonPerson関係を通常のJOINからそれ自体のエンティティに昇格させる必要があることを説明しています。ただし、自己結合の場合は何を説明しません。違いは、すべての関係者に Dave Dee (ID = 1)を要求すると、 Tich (ID = 5)を取得するだけでなく、 Dave DeeもRelatedPersonID列にあるため、 Dozy (ID = 2)も取得します。
これまでの私のソリューションは、Personクラスに2つのプロパティを設定することです。
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クラスにはコレクションが1つしか残っていません。これは、このPersonが持つ関係を表すPersonPersonオブジェクトのコレクションです。 特定のPersonと関係を持つPersonsを取得するには、Personクラスのコレクションにそれらを持たせる代わりに、PersonRepositoryにそれらのPersonsを返す特定のメソッドを作成できます。これによりパフォーマンスも向上すると思います。
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-本で言っていることでもあります:場合によっては、関連オブジェクト(=関連オブジェクト)を持ち歩く代わりに、関連オブジェクトへのアクセスを提供できる特別なメソッドをリポジトリに用意する方がよいオブジェクト自体で。
コードをテストしなかったので、ここに入力しただけなので、構文エラーなどもチェックしませんでしたが、これがどのように見えるかについて少し明確にする必要があると思います。