ドメインモデルを尊重するという約束に誘惑されて、永続性のためにNhibernateを使い始めたので、ドメインオブジェクトの関係マネージャーを実装しようとしました。基本的に、双方向の 1 対多および多対多の関係の管理に関してコードを DRY するために、これらの関係を別のクラスで管理することにしました。一対多または多対一のプロパティが設定されている場合、ディクショナリに 2 つのオブジェクトのエントリが作成されます。キーは、多面を保持するコレクション値を持つ一面、または値を持つ多面のいずれかです。片側。
タイプの特定の組み合わせに対する 1 対多の関係は次のようになります。
public class OneToManyRelation<TOnePart, TManyPart> : IRelation<IRelationPart, IRelationPart>
where TOnePart : class, IRelationPart
where TManyPart : class, IRelationPart
{
private readonly IDictionary<TOnePart, Iesi.Collections.Generic.ISet<TManyPart>> _oneToMany;
private readonly IDictionary<TManyPart, TOnePart> _manyToOne;
public OneToManyRelation()
{
_manyToOne = new ConcurrentDictionary<TManyPart, TOnePart>();
_oneToMany = new ConcurrentDictionary<TOnePart, Iesi.Collections.Generic.ISet<TManyPart>>();
}
public void Set(TOnePart onePart, TManyPart manyPart)
{
if (onePart == null || manyPart == null) return;
if (!_manyToOne.ContainsKey(manyPart)) _manyToOne.Add(manyPart, onePart);
else _manyToOne[manyPart] = onePart;
}
public void Add(TOnePart onePart, TManyPart manyPart)
{
if (onePart == null || manyPart == null) return;
if (!_manyToOne.ContainsKey(manyPart)) _manyToOne.Add(manyPart, onePart);
else _manyToOne[manyPart] = onePart;
if (!_oneToMany.ContainsKey(onePart)) _oneToMany.Add(onePart, new HashedSet<TManyPart>());
_oneToMany[onePart].Add(manyPart);
}
public Iesi.Collections.Generic.ISet<TManyPart> GetManyPart(TOnePart onePart)
{
if (!_oneToMany.ContainsKey(onePart)) _oneToMany[onePart] = new HashedSet<TManyPart>();
return _oneToMany[onePart];
}
public TOnePart GetOnePart(TManyPart manyPart)
{
if(!_manyToOne.ContainsKey(manyPart)) _manyToOne[manyPart] = default(TOnePart);
return _manyToOne[manyPart];
}
public void Remove(TOnePart onePart, TManyPart manyPart)
{
_manyToOne.Remove(manyPart);
_oneToMany[onePart].Remove(manyPart);
}
public void Set(TOnePart onePart, Iesi.Collections.Generic.ISet<TManyPart> manyPart)
{
if (onePart == null) return;
if (!_oneToMany.ContainsKey(onePart)) _oneToMany.Add(onePart, manyPart);
else _oneToMany[onePart] = manyPart;
}
public void Clear(TOnePart onePart)
{
var list = new HashedSet<TManyPart>(_oneToMany[onePart]);
foreach (var manyPart in list)
{
_manyToOne.Remove(manyPart);
}
_oneToMany.Remove(onePart);
}
public void Clear(TManyPart manyPart)
{
if (!_manyToOne.ContainsKey(manyPart)) return;
if (_manyToOne[manyPart] == null) return;
_oneToMany[_manyToOne[manyPart]].Remove(manyPart);
_manyToOne.Remove(manyPart);
}
}
多くの側では、コード スニペットは次のようになります。
public virtual SubstanceGroup SubstanceGroup
{
get { return RelationProvider.SubstanceGroupSubstance.GetOnePart(this); }
protected set { RelationProvider.SubstanceGroupSubstance.Set(value, this); }
}
一方で、この場合、SubstanceGroup のスニペットは次のようになります。
public virtual ISet<Substance> Substances
{
get { return RelationProvider.SubstanceGroupSubstance.GetManyPart(this); }
protected set { RelationProvider.SubstanceGroupSubstance.Set(this, value); }
}
ドメインオブジェクトを使用するだけで、これはうまく機能します。ドメイン オブジェクトでは、適切なリレーションを取得する抽象ファクトリを参照するだけで、一方からリレーションを設定できるため、自動的に双方向になります。
ただし、NH が問題を引き起こすと、辞書に重複したキーが作成されます。どういうわけか NH は、ドメイン オブジェクトの新しいコピー (?) を使用して null 値 (!) を持つリレーション プロパティを設定します。したがって、ドメイン オブジェクトが保存されると、そのドメイン オブジェクトの 2 つのエントリが、たとえば関係の多側、つまり _manyToOne ディクショナリに作成されます。
この問題で髪の毛が抜けてしまいます。何が起こっているのかわかりません??