これはおそらく、アイデンティティ マップのどこかに違反していることを意味します。これは、同じデータベース ID を持つオブジェクトの 2 つのインスタンスがぶらぶらしていることを意味しますが、参照 ID は異なります。
Session.Contains は参照の等価性をチェックしますが、セッション内に同じ型と ID を持つものが既に存在する場合、Lock は例外をスローします。これは厳密な比較ではありません。
Equals と GetHashCode の単純な実装 (非常に単純で推奨されない) を使用して、AdventureWorks データベースで次のテストを検討してください。
using (ISession session = SessionFactory.Factory.OpenSession())
{
int someId = 329;
Person p = session.Get<Person>(someId);
Person test = new Person() { BusinessEntityID = someId };
Assert.IsTrue(p.Equals(test)); //your code might think the objects are equal, so you'd probably expect the next line to return true
Assert.IsFalse(session.Contains(test)); //But they're not the same object
Assert.Throws<NonUniqueObjectException>(() =>
{
session.Lock(test, LockMode.None); //So when you ask nhibernate to track changes on both objects, it gets very confused
});
}
NHibernate (および ORM だと思います) は、オブジェクトへの変更を追跡することで機能します。そのため、Get'ing Person 329 では、Hibernate に Person の特定のインスタンスに何が起こっても注意を払うように依頼します。彼の名をハイメに変更するとしましょう。
次に、同じ Id を持つ person の別のインスタンスを取得します (この場合、新しく作成しただけですが、そのようなオブジェクトを取得するための多くの陰湿な方法があります)。NHibernate でこれをセッションにもアタッチできると想像してみてください。この 2 番目のオブジェクトの名を Robb のような名前に設定することもできます。
セッションをフラッシュするとき、NHibernate には、データベースの行を Robb または Jaime のいずれかに同期する必要があるかどうかを知る方法がありません。したがって、それが発生する前に、一意でない方法をスローします。
理想的には、これらの状況が発生しないようにする必要がありますが、何が起こっているのかがよくわかっている場合は、session.Merge を確認してください。これにより、追跡された状態を、最後にマージされたものに強制できます (例では Robb)。 .