我正在尝试更新的对象包含(除其他外)IList的子对象,映射的内容如下:
<bag name="PlannedSlices" inverse="true" cascade="all-delete-orphan"> <key column="JobNumber" /> <one-to-many class="SliceClass" /> </bag>
我们已经安排了我们的MVC编辑视图表单,以便当它被回发时,我们的操作方法被传递给对象(包括子项的List<>)我们通过表单正确地往返所有实体ID.
我们对post动作方法的天真尝试执行session.SaveOrUpdate(parentObject),其中parentObject已被默认的modelbinder从视图窗体中删除.
对于以下任何一种情况,这似乎都可以正常工作:
>创建新的父对象
>修改父级的属性
>添加新的子对象
>修改现有子对象
(查看nHibernate日志,我可以看到它正确建立对象是新的还是现有的,并发出适当的UPDATE或INSERT)
失败的情况是:
– 删除子对象 – 即如果它们不在IList中,则不会从数据库中删除它们.没有例外或任何事情,他们只是不被删除.
我的理解是,这是因为nHibernate为创建需要删除的子列表而执行的魔法不适用于分离的实例.
我无法找到一个简单的例子,说明使用nHibernate这种行为方法应该是什么样的(即使用模型绑定器对象作为分离的nHibernate实例) – 基于MS EF的示例(例如http://stephenwalther.com/blog/archive/2009/02/27/chapter-5-understanding-models.aspx)似乎使用了方法’ApplyPropertyChanges’将已更改的属性从模型绑定对象复制到重新加载的实体实例.
所以,经过这一切,问题很简单 – 如果我有模型绑定器给我一个包含子对象集合的新对象,我应该如何通过nHibernate更新它(其中’update’包括可能删除子节点)?
解决方法
鉴于以下“域”类:
public class Person { private IList<Pet> pets; protected Person() { } public Person(string name) { Name = name; pets = new List<Pet>(); } public virtual Guid Id { get; set; } public virtual string Name { get; set; } public virtual IEnumerable<Pet> Pets { get { return pets; } } public virtual void AddPet(Pet pet) { pets.Add(pet); } public virtual void RemovePet(Pet pet) { pets.Remove(pet); } } public class Pet { protected Pet() { } public Pet(string name) { Name = name; } public virtual Guid Id { get; set; } public virtual string Name { get; set; } }
使用以下映射:
public class PersonMap : ClassMap<Person> { public PersonMap() { LazyLoad(); Id(x => x.Id).GeneratedBy.GuidComb(); Map(x => x.Name); HasMany(x => x.Pets) .Cascade.AllDeleteOrphan() .Access.AsLowerCaseField() .SetAttribute("lazy","false"); } } public class PetMap : ClassMap<Pet> { public PetMap() { Id(x => x.Id).GeneratedBy.GuidComb(); Map(x => x.Name); } }
这个测试:
[Test] public void CanDeleteChildren() { Person person = new Person("joe"); Pet dog = new Pet("dog"); Pet cat = new Pet("cat"); person.AddPet(dog); person.AddPet(cat); Repository.Save(person); UnitOfWork.Commit(); CreateSession(); UnitOfWork.BeginTransaction(); Person retrievedPerson = Repository.Get<Person>(person.Id); Repository.Evict(retrievedPerson); retrievedPerson.Name = "Evicted"; Assert.AreEqual(2,retrievedPerson.Pets.Count()); retrievedPerson.RemovePet(retrievedPerson.Pets.First()); Assert.AreEqual(1,retrievedPerson.Pets.Count()); Repository.Save(retrievedPerson); UnitOfWork.Commit(); CreateSession(); UnitOfWork.BeginTransaction(); retrievedPerson = Repository.Get<Person>(person.Id); Assert.AreEqual(1,retrievedPerson.Pets.Count()); }
DeletingChildrenOfEvictedObject.CanDeleteChildren:已通过
NHibernate:INSERT INTO [Person](Name,Id)VALUES(@ p0,@ p1); @ p0 =’joe’,@ p1 =’cd123fc8-6163-42a5-aeeb-9bf801013ab2′
NHibernate:INSERT INTO [Pet](Name,@ p1); @ p0 =’dog’,@ p1 =’464e59c7-74d0-4317-9c22-9bf801013abb’
NHibernate:INSERT INTO [Pet](Name,@ p1); @ p0 =’cat’,@ p1 =’010c2fd9-59c4-4e66-94fb-9bf801013abb’
NHibernate:UPDATE [Pet] SET Person_id = @ p0 WHERE Id = @ p1; @ p0 =’cd123fc8-6163-42a5-aeeb-9bf801013ab2′,@ p1 =’464e59c7-74d0-4317-9c22-9bf801013abb’
NHibernate:UPDATE [Pet] SET Person_id = @ p0 WHERE Id = @ p1; @ p0 =’cd123fc8-6163-42a5-aeeb-9bf801013ab2′,@ p1 =’010c2fd9-59c4-4e66-94fb-9bf801013abb’
NHibernate:SELECT person0_.Id as Id5_0_,person0_.Name as Name5_0_ FROM [Person] person0_ WHERE person0_.Id=@p0; @ p0 =’cd123fc8-6163-42a5-aeeb-9bf801013ab2′
NHibernate:SELECT pets0_.Person_id as Person3_1_,pets0_.Id as Id1_,pets0_.Id as Id6_0_,pets0_.Name as Name6_0_ FROM [Pet] pets0_ WHERE pets0_.Person_id=@p0; @ p0 =’cd123fc8-6163-42a5-aeeb-9bf801013ab2′
NHibernate:UPDATE [Person] SET Name = @ p0 WHERE Id = @ p1; @ p0 =’被驱逐’,@ p1 =’cd123fc8-6163-42a5-aeeb-9bf801013ab2′
NHibernate:UPDATE [Pet] SET Name = @ p0 WHERE Id = @ p1; @ p0 =’dog’,@ p1 =’464e59c7-74d0-4317-9c22-9bf801013abb’
NHibernate:UPDATE [Pet] SET Person_id = null WHERE Person_id = @ p0 AND Id = @ p1; @ p0 =’cd123fc8-6163-42a5-aeeb-9bf801013ab2′,@ p1 =’010c2fd9-59c4-4e66-94fb-9bf801013abb’
NHibernate:DELETE FROM [Pet] WHERE Id = @ p0; @ p0 =’010c2fd9-59c4-4e66-94fb-9bf801013abb’
NHibernate:SELECT person0_.Id as Id5_0_,pets0_.Name as Name6_0_ FROM [Pet] pets0_ WHERE pets0_.Person_id=@p0; @ p0 =’cd123fc8-6163-42a5-aeeb-9bf801013ab2′
注意DELETE FROM [Pet] ……
所以,你需要能够做的是手工操作一个Person对象(在这个例子中)与修改后的集合,它应该能够确定要删除的内容.确保设置了Cascade.AllDeleteOrphan()属性.