-1

ドメインモデルを尊重するという約束に誘惑されて、永続性のために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 ディクショナリに作成されます。

この問題で髪の毛が抜けてしまいます。何が起こっているのかわかりません??

4

2 に答える 2

3

「NHibernate は本当に透過的な永続性を提供しますか」という最初の非常に一般的な質問に答えるために、私はただ言うことができます。完璧なものはありません。NH は、可能な限り透明性を保つために最善を尽くします。また、複雑さを可能な限り低く保つことも試みています。

特にコレクションに関して、いくつかの仮定があります。 コレクションとその実装は、ドメイン モデルの一部とは見なされません。NH は独自のコレクション実装を提供します。ISetやのようなインターフェースを使用することだけが期待されているわけではありませんIList。また、オブジェクトがデータベースから読み取られたときに NH によって指定されたインスタンスを取得し、それを独自のものに置き換えてはなりません。(リレーション クラスが実際に何に使用されているかはわかりません。そのため、これが問題かどうかはわかりません。)

ドメイン オブジェクトは、セッションの同じインスタンス内で一意です。毎回ドメイン オブジェクトの新しいインスタンスを取得する場合は、おそらく、データベースの対話ごとに新しいセッションを作成する "session-per-call" アンチパターンを実装したことになります。

あなたが実際に何をしているのか、私にはわかりません。この OneToManyRelation は実際にどのように使用されますか? NH が期待どおりに動作しない場合、あなたは何をしていますか? これは、特定の実装に固有の問題です。

于 2011-08-24T12:18:15.253 に答える
0

「複雑なコード」と「一体何をしているのですか」に関するコメントに加えて。問題は、以下のコード スニペットのように NH の永続コレクションを置き換えていたことです。

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

多くの部分の新しいハッシュ セットを作成します。そして、それが問題でした。入ってくるコレクションで多くの部分を設定しただけの場合(NHの永続コレクション実装の場合)、それはうまくいったでしょう。

NH の初心者として、コレクションを NH の特別な実装に置き換えることは、エラーの重要な原因でした。他のNH初心者への警告として。

于 2011-08-26T09:30:49.830 に答える