NHibernate真的能提供透明的持久性吗?

时间:2011-08-24 11:35:43

标签: nhibernate bidirectional-relation

开始使用Nhibernate来表示它尊重你的域模型的承诺所引发的持久性,我试图为我的域对象实现一个关系管理器。基本上,为了管理关于管理双向一对多和多对多关系的代码,我决定让这些关系由一个单独的类管理。当设置一对多或多对一属性时,在字典中创建两个对象的条目,该键可以是具有用于保持多个边的集合值的一侧,或者是具有值的多个边的多边。一边。

特定类型组合的一对多关系如下所示:

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使用域值对象的新副本(?)设置具有空值(!)的关系属性。因此,当域对象被保存时,我有两个该域对象的条目,例如关系的许多方面,即_manyToOne字典。

这个问题让我失去了头发,我不知道发生了什么?

2 个答案:

答案 0 :(得分:3)

回答你的第一个非常笼统的问题:“NHibernate真的能提供透明的持久性”,我只能说:没有什么是完美的。 NH尽量保持透明度,同时尽量保持其复杂性尽可能低。

有一些假设,尤其是关于集合的假设:集合及其实现不被视为您的域模型的一部分。 NH提供自己的集合实现。您不仅需要使用ISetIList等接口。当从数据库中读取对象时,您还应该使用NH给出的实例,而不是用您自己的对象替换它。 (我不知道你的关系类实际用于什么,所以我不知道这是否是这里的问题。)

域对象在会话的同一实例中是唯一的。如果每次都获得域对象的新实例,则可能实现了“每次调用会话”反模式,这为每个数据库交互创建了一个新会话。

我不清楚你到底在做什么。这个OneToManyRelation实际上是如何使用的?当NH不按预期行事时,你在做什么?这是您具体实施的一个非常具体的问题。

答案 1 :(得分: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);
}

我为很多部分创建了一个新的Hashed集。这就是问题所在。如果只是设置了许多部分与集合进入(在NH的持久性集合实现的情况下),而不是它可以工作。

作为NH新手,用NH的特殊实现替换集合一直是错误的重要来源。就像对其他NH新手的警告一样。