【发布时间】:2011-08-24 11:35:43
【问题描述】:
开始使用 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);
}
}
在许多方面,代码 sn-p 看起来像:
public virtual SubstanceGroup SubstanceGroup
{
get { return RelationProvider.SubstanceGroupSubstance.GetOnePart(this); }
protected set { RelationProvider.SubstanceGroupSubstance.Set(value, this); }
}
一方面,在这种情况下,SubstanceGroup,sn-p 看起来像:
public virtual ISet<Substance> Substances
{
get { return RelationProvider.SubstanceGroupSubstance.GetManyPart(this); }
protected set { RelationProvider.SubstanceGroupSubstance.Set(this, value); }
}
仅使用我的域对象,这非常有效。在域对象中,我只需要引用一个抽象工厂来检索适当的关系,我可以从一侧设置关系,从而自动成为双向的。
但是,当 NH 出现问题时,我的字典中有重复的键。 NH 以某种方式设置了一个具有空值(!)的关系属性和一个域对象的新副本(?)。因此,当域对象被保存时,我有该域对象的两个条目,例如关系的多方面,即 _manyToOne 字典。
这个问题让我脱发,我不明白这是怎么回事??
【问题讨论】:
-
我不太明白你在做什么。这个关系类是您在实体中用作字段的类型吗?它是如何映射的?您如何创建、读取和更改数据?
-
我的问题不完整,我已经编辑并添加了一个代码 sn-p 我如何使用这个关系类。顺便说一句,关系类表示两个对象之间的关系,就是这样。在一对多关系的情况下,有一个部分和多个部分。
-
似乎是一种管理双向关系的复杂方式。似乎此解决方案存在问题。我认为使用添加/删除方法手动管理实体内部的这些关系会更容易。
-
我听从了您的建议,并恢复到内部私有字段来存储对父对象和子对象的引用。确实,这个问题似乎已经解决了。但是,我仍然很困惑为什么上述解决方案不起作用,不管是否复杂(虽然确实为我节省了大约 500 行代码)。
标签: nhibernate bidirectional-relation