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

NHibernate真的提供透明的持久性吗,nhibernate,bidirectional-relation,Nhibernate,Bidirectional Relation,我开始使用Nhibernate实现持久性,因为它承诺尊重您的域模型,所以我尝试为我的域对象实现一个关系管理器。基本上,为了擦干关于管理双向一对多和多对多关系的代码,我决定让这些关系由一个单独的类来管理。当设置一对多或多对一属性时,在字典中为这两个对象创建一个条目,该键要么是具有集合值的一侧以容纳多个边,要么是具有值的一侧的多个边 特定类型组合的一对多关系如下所示: public class OneToManyRelation<TOnePart, TManyPart> : IRelat

我开始使用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);
    }
}
一方面,在本例中是SubstanceGroup,代码段如下所示:

    public virtual SubstanceGroup SubstanceGroup
    {
        get { return RelationProvider.SubstanceGroupSubstance.GetOnePart(this); } 
        protected set { RelationProvider.SubstanceGroupSubstance.Set(value, this); }
    }
    public virtual ISet<Substance> Substances
    {
        get { return RelationProvider.SubstanceGroupSubstance.GetManyPart(this); }
        protected set { RelationProvider.SubstanceGroupSubstance.Set(this, value); }
    }
公共虚拟ISet物质
{
get{return RelationProvider.SubstanceGroupSubstance.GetManyPart(this);}
受保护集{RelationProvider.SubstanceGroupSubstance.set(this,value);}
}
仅使用我的域对象,效果就非常好。在域对象中,我只需要引用一个抽象工厂来检索适当的关系,我可以从一侧设置关系,这样它就会自动变成双向的

然而,当NH介入时,问题是我的字典中有重复的键。NH将一个关系属性设置为空值(!)和域对象的新副本(?)。因此,当域对象被保存时,我有两个域对象的条目,例如关系的多个方面,即_manytonedictionary


这个问题让我头发脱落,我不明白发生了什么事???

回答你的第一个非常普遍的问题:“NHibernate真的能提供透明的持久性吗”,我只能说:没有什么是完美的。NH尽最大努力做到尽可能透明,同时也尽可能降低复杂性

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

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


我不知道你到底在做什么。这种“一人一人”的关系实际上是如何使用的?当NH的表现不符合预期时,你在做什么?这对于您的具体实现来说是一个非常具体的问题。

除了关于“卷积代码”和“您到底在做什么”的注释之外。问题是,我正在替换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);
}
public void Add(TOnePart onePart,TManyPart manyPart)
{
if(onePart==null | | manyPart==null)返回;
如果(!\u manyToOne.ContainsKey(manyPart))\u manyToOne.Add(manyPart,onePart);
else _manytone[许多部分]=一部分;
如果(!\u oneToMany.ContainsKey(onePart))\u oneToMany.Add(onePart,new HashedSet());
_一件事[一件事]。添加(多件事);
}
我为多部分创建了一个新的散列集。这就是问题所在。如果只是设置了集合的多个部分(在NH的持久性集合实现的情况下),那么它就可以工作了


作为NH的新手,用NH的特殊实现替换集合是错误的一个重要来源。作为对其他新罕布什尔州新手的警告。

我真的不明白你在做什么。此关系类是您在实体中用作字段的类型吗?它是如何映射的?您是如何创建、读取和更改数据的?我的问题不完整,我已经编辑并添加了一段代码片段,说明如何使用此关系类。顺便提一下,Relationship类表示两个对象之间的关系,就是这样。在一对多关系的情况下,有一部分和多部分。似乎是管理双向关系的复杂方式。似乎这个解决方案是开放的问题。我认为使用添加/删除方法在实体内部手动管理这些关系更容易。我已经按照您的建议,返回到内部私有字段以存储对父对象和子对象的引用。事实上,这似乎解决了问题。但是,我仍然感到困惑,为什么上面的解决方案不起作用,复杂与否(虽然为我节省了大约500行代码)。感谢您努力理解我的代码。关于您关于集合不被视为域模型的一部分的评论,我强烈反对,但我已经知道NH是如何处理的。因此,我尊重地接受NH对集合的所有引用,并将它们存储在我的关系类中,因为您可以看到许多部分是Iesi.collections.Generic.ISet。此外,我不使用每次调用会话的反模式,但我将会话视为一个工作单元。一旦完成了工作,会话就结束了,我的域模型也结束了,客户也会被告知这方面的成果。最后,我不认为在域模型中管理一对多关系和多对多关系是一个非常具体的问题。我刚刚注意到,使用特殊的set和get方法处理域类中的本地字段会导致重复代码。如果父母有一个孩子,而孩子应该知道自己是父母,那么双方都应该有一些代码。我的代码中有很多父子关系。与多对多关系一样,这个重复的代码也非常简单。所以