数日間、質問に対する答えを探していて、ほとんどのヒント/答えを試し、nHibernate によって生成された何百行ものログを調べましたが、マッピングを機能させる方法が見つかりません。 .. nHiberante は、SaveOrUpdate を要求されたときに、delete ステートメントの生成を拒否します。
Person、House、Room の 3 つの個別のエンティティがあります。実際には、各 Person には 1 つの家があり、各家には複数の部屋があり、家と部屋の間のマッピングは予想どおり 1 対多です。 Person と House の間の 1 対 1 のマッピングが必要だったので、流暢な nHibernate wiki を読みました。これは、データベース レベルで複数の人が同じものを共有するのを止めるものは何もないため、実際には多 (人) 対 1 (家) であると書かれています家。それは理にかなっていると思うので、データベース スキーマとクラス マップを以下のように設計しました。
CREATE TABLE [dbo].[Person](
[PersonId] [int] IDENTITY(1,1) NOT NULL,
[HouseId] [int] NOT NULL
)
CREATE TABLE [dbo].[House](
[HouseId] [int] IDENTITY(1,1) NOT NULL,
[HouseName] [varchar](500) NOT NULL
)
CREATE TABLE [dbo].[Room](
[RoomId] [int] IDENTITY(1,1) NOT NULL,
[HouseId] [int] NOT NULL
)
HouseId on [Person] and [Room] are both foreign keys referencing HouseId from [House] table
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("Person")
Id (x=>x.Id).Column("PersonId").GeneratedBy.Identity().UnsavedValue(0);
Reference(x=>x.House).Column("HouseId").ForeignKey("HouseId").Not.LazyLoad().Cascade.All();
}
}
public class HouseMap : HouseMap<Room>
{
public RoomMap()
{
Table("House")
Id (x=>x.Id).Column("HouseId").GeneratedBy.Identity().UnsavedValue(0);
HasMany(x => x.Persons)
.KeyColumn("HouseId")
.Not.LazyLoad()
.Cascade.AllDeleteOrphan().Inverse();
HasMany(x => x.Rooms)
.KeyColumn("HouseId")
.Not.LazyLoad()
.Cascade.AllDeleteOrphan().Inverse();
}
}
public class RoomMap : ClassMap<Room>
{
public RoomMap()
{
Table("Room")
Id (x=>x.Id).Column("RoomId").GeneratedBy.Identity().UnsavedValue(0);
Reference(x=>x.House).Column("HouseId").ForeignKey("HouseId").Not.LazyLoad().Cascade.All();
}
}
クラス House はそれぞれ Person と Room の 2 つのリストを保持し、クラス Person と Room は両方とも House オブジェクトへの参照を保持します。
そこで、House のインスタンスを作成し、その 2 つの子リスト Person(1 つ 1) と Room(複数の部屋) を設定し、Person/Rooms のプロパティを House にリンクし、session.SaveOrUpdate を使用して Person をすべて保存します。うまくいきます。
次に、Person のインスタンスを取得して、Person.House.List から部屋を削除し、room.House を null に設定して参照を完全に解除し、session.SaveOrUpdate を使用して Person を再度保存します (Person A と呼びましょう)。 、nHibernate は、親の家にリンクされていない孤立した部屋を削除する削除ステートメントを正しく生成しました..これまでのところとても良いです...
ここで私の問題が始まります...私のアプリケーションでは、オブジェクトPersonをPersonContractに変換してからクライアントに送信する必要があります。クライアントには、roomContractをPersonContract.HouseContractから削除し、PersonContractのコピーを送信する機能があります戻る。PersonContract には双方向の参照がないことに注意してください。つまり、PersonContract には HouseContract のプロパティが 1 つあり、HouseContract には RoomContract のリストしか含まれていないため、HouseContract は PersonContract のリストを保持せず、RoomContract には HouseContract プロパティがありません。
次にサーバー側で、PersonContract を元の Person オブジェクトに変換します。コンバーターは、Person、Room、家のすべての新しいインスタンスを作成し、ID を設定して、それらの間のすべての双方向参照を構築します。
問題は...次に Person オブジェクトで session.SaveOrUpdate を使用すると (コントラクトの変換後)、nHibernate は部屋がコレクションから削除されたという事実を無視しているように見えますが、更新または挿入のジョブは適切に実行されます..
コントラクト コンバーターを注意深くチェックして、すべてのプロパティ/参照/ID が適切に設定されていることを確認しました。上記の人物 A とまったく同じコピーに見えます...しかし、nHibernate がここで仕事をしていない理由がわかりません.
Person/Room/House の Equals と GetHashCode の両方をオーバーライドして、ID を返しました。
私が観察したもう 1 つの事実は、変換後の Person オブジェクトで session.SaveOrUpdate を使用すると、値が実際には変更されていないにもかかわらず、nHibernate が多くの Update ステートメントを生成していることです。 Person オブジェクトなので、ID を使用して識別してすべてを更新することを選択します...
私は nHibernate にまったく慣れていないので、ここでひどく間違ったことをしている場合はお知らせください:)
更新:ハウスとルームの間の1対多のマッピングから逆を削除すると、nHibernateは削除されたルームのハウスIDをNULLに設定します...ルームをまったく削除しないよりはましですが、孤立した部屋を削除したいですDB..
更新 2 : session.Merge on Person は機能しています (SaveOrUpdate は動作しません)...理由はわかりませんが..