DotNet 4.5 の NHibernate で多対多の関係を正しく使用する方法を理解しようとしています。使用するコレクション タイプと初期化するコレクション タイプ。
シナリオ: 組織単位と役職のモデル化。次のテーブルには多対多の関係があります。
OrgUnits の位置 OrgUnitPositions
私は双方向セッターを使用しているので、orgUnit.AddPosition(position) OR 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.Positions.Contains(this)) のこの行が false を返すことです。組織単位が含まれています。これにより、2 回追加され、保存時に重複キーの例外が発生します。
私はあらゆる種類のことを試しました (リスト/IList の元のコレクション) が、私のコレクション タイプ (ilist/iset/etc) が原因であると思われます - 誰かが使用するコレクション タイプの方向を教えてくれることを願っています。それらを何として初期化するかなど
position.AddOrgUnit(orgUnit); を呼び出すと、orgUnit.AddPosition の代わりに、すべてが機能します。
- - アップデート
コメントに応えて、Position.cs の AddOrgUnit を次のように変更しました。
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
this.GetHashCode() 53416668
この.ID {8386857d-a52e-4f17-8094-a231003299b5}
p.ID {8386857d-a52e-4f17-8094-a231003299b5}
p .説明「ボブ」
this .説明「ボブ」
p.description = "ジェーン"
this.Description "ジェーン"
p.description "ジェーン"
これは奇妙なものです!hashCode は異なりますが、同じオブジェクトの同じインスタンスのように見えます。IE、「this」バージョンでプロパティを変更すると、「p」バージョンで変更されます。
最後に、ReferenceEquals(p.ReportsToPosition, this.ReportsToPosition) true
それらが同じ休止状態セッションからのものであることを示す傾向があるのはどれですか? (つまり、両親は同じです)
これが私の呼び出しコードです。IoC に StrctureMap を使用し、ISession をリポジトリに挿入します。BLL を渡すようにコードを変更しました。これにより、リポジトリが 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);
}