在 nHibernate 中执行双向多对多的正确程度
本文关键字:程度 nHibernate 执行 | 更新日期: 2023-09-27 18:30:23
我试图弄清楚如何在 DotNet 4.5 中的 NHibernate 中正确使用多对多关系。要使用的集合类型以及将它们初始化为什么。
场景:对组织单位和职位进行建模。与下表之间存在多对多关系:
组织单位位置组织单位职位
我正在使用双向二传手,因此无论我去orgUnit.AddPosition(position)还是position。AddOrgUnit(orgUnit),该关系应添加到两个集合中。
这是我的示例代码
public class Position
{
public Position()
{
OrgUnits = new List<OrgUnit>();
}
public virtual ICollection<OrgUnit> OrgUnits { get; set; }
public virtual void AddOrgUnit(OrgUnit orgUnit)
{
if (!OrgUnits.Contains(orgUnit))
{
OrgUnits.Add(orgUnit);
if (!orgUnit.Positions.Contains(this))
orgUnit.AddPosition(this);
}
}
public class PositionMap : ClassMap<Position>
{
public PositionMap()
{
HasManyToMany(x => x.OrgUnits)
.ChildKeyColumn("OrgUnitID")
.ParentKeyColumn("PositionID")
.Table("OrgUnitPositions")
.Fetch.Select()
.Cascade.AllDeleteOrphan()
.AsBag()
.Inverse();
}
}
public class OrgUnit
{
public OrgUnit()
{
Positions = new HashSet<Position>();
}
public virtual ICollection<Position> Positions { get; set; }
public virtual void AddPosition(Position value)
{
if (!Positions.Contains(value))
{
Positions.Add(value);
if (!value.OrgUnits.Contains(this))
value.AddOrgUnit(this);
}
}
}
public class OrgUnitMap : ClassMap<OrgUnit>
{
public OrgUnitMap()
{
HasManyToMany(x => x.Positions)
.ChildKeyColumn("PositionID")
.ParentKeyColumn("OrgUnitID")
.Table("OrgUnitPositions")
.Fetch.Select()
.Cascade.AllDeleteOrphan()
.AsSet()
;
}
}
我的问题是,当我调用orgUnit.AddPosition(position))时,在某些情况下,Position.AddOrgUnit中的这一行:if (!value.Position.Contains(this)) 返回 false,即使我可以在调试器中看到它确实包含组织单位。这会导致它被添加两次,然后在保存时发生重复键异常。
我已经尝试了各种各样的东西(我的原始集合,其中列表/ILists),但我怀疑我的集合类型(ilist/iset/等)是原因 - 希望有人可以指出我使用哪些集合类型的方向,启动它们是什么,等等。
如果我打电话给位置。AddOrgUnit(orgUnit);而不是orgUnit.AddPosition,一切正常。
---更新
为了回应评论,我将AddOrgUnit更改为Place.cs如下所示:
public virtual void AddOrgUnit(OrgUnit orgUnit)
{
if (!OrgUnits.Contains(orgUnit))
{
OrgUnits.Add(orgUnit);
foreach (Position p in orgUnit.Positions)
{
System.Console.WriteLine(string.Format("{0}", ReferenceEquals(p, this)));
}
if (!value.Positions.Contains(this))
{
value.AddPosition(this);
}
}
}
这是我发现的 - 请记住,orgUnit.positions 只包含 1 个项目,而 for-each 只是为了在调试器中访问该项目。
ReferenceEquals(p, this) false
p.GetHashCode() 40148707
这。GetHashCode() 53416668
this.ID {8386857d-a52e-4f17-8094-a231003299b5}
p.ID {8386857d-a52e-4f17-8094-a231003299b5}
p.描述 "鲍勃"
这。描述 "鲍勃"
p.描述 = "简"
这。描述 "简"
p.描述 "简"
这是奇怪的! 哈希码是不同的,但它似乎是同一对象的相同实例。 IE,如果我更改"this"版本上的属性,它会在"p"版本中更改。
最后ReferenceEquals(p.ReportsToPosition, this.报告到位置) 真
哪个倾向于表明它们来自同一个休眠会话?(即父母是一样的)
这是我的调用代码,我们使用 StrctureMap for IoC,将 ISession 注入存储库中。 我已经更改了我的代码以传入 BLL,这保证了相同的 Isession,因为存储库已注入其中。
奇怪的是,导致错误的唯一行是指示的行,如果我删除上面的行,它也可以完美运行!
[TestMethod]
public void CreateExampleOrgStructure()
{
OrgStructureLogic osl = (OrgStructureLogic)ObjectFactory.GetInstance(typeof(OrgStructureLogic));
Domain domain = osl.GetNewDomain(DomainTypeEnum.ReportingLines);
domain.Name = string.Format("blah.Net - {0} {1}", DateTime.Now.ToLongDateString(), DateTime.Now.ToLongTimeString());
OrgUnit ouGlobal = domain.RootOrgUnit;
ouGlobal.Name = "Global";
OrgUnit ouTech = GetNewOrgUnit(ouGlobal, "Technology", osl);
AddPositionToOrgUnit(ouTech, "Chief Technology Officer", osl);
AddPositionToOrgUnit(ouTech, "General Manager Technology", osl);
OrgUnit ouFeatureDevelopers = GetNewOrgUnit(ouTech, "Feature Dev", osl);
AddPositionToOrgUnit(ouFeatureDevelopers, "Senior Feature Developer", osl);
AddPositionToOrgUnit(ouFeatureDevelopers, "Feature Developer", osl);
OrgUnit ouSupportDevelopers = GetNewOrgUnit(ouTech, "Support Dev", osl);
AddPositionToOrgUnit(ouSupportDevelopers, "Senior Support Developer", osl);
AddPositionToOrgUnit(ouSupportDevelopers, "Support Developer", osl);
OrgUnit ouDevManagement = GetNewOrgUnit(ouTech, "Management", osl);
AddPositionToOrgUnit(ouDevManagement, "Dev Manager", osl); /* This is the problem!!!!*/
AddPositionToOrgUnit(ouDevManagement, "Application Architect", osl);
osl.SaveDomain(domain);
}
更奇怪的是,我还可以解决问题
private void AddPositionToOrgUnit(OrgUnit orgUnit, string positionName, OrgStructureLogic osl)
{
orgUnit.AddPosition(GetPositionByName(positionName), osl);
}
自
private void AddPositionToOrgUnit(OrgUnit orgUnit, string positionName, OrgStructureLogic osl)
{
Position position = GetPositionByName(positionName, osl);
position.AddOrgUnit(orgUnit);
}
在堆栈跟踪之后,GetHashCode 的值在此处更改:
if (!position.OrgUnits.Contains(this))
{
position.AddOrgUnit(this);
}
即,位置。GetHashCode() = "a",但是当我进入位置时。添加组织单位,这个。GetHashCode() 是 "b"。
查看堆栈跟踪,
[Native to Managed Transition] NHibernate.dll!NHibernate.Proxy.DefaultLazyInitializer.Intercept(NHibernate.Proxy.DynamicProxy.InvocationInfo info = {NHibernate.Proxy.DynamicProxy.InvocationInfo}) + 0x14d bytes
PositionProxyModule.mod!PositionProxy.AddOrgUnit() + 0x1f2 bytes
看来 nHibernate 代理正在成为障碍。 因此,sl3dg3是正确的,解决方案是覆盖Equals和GetHashCode。