【问题标题】:What's the proper way to update an nhibernate entity from a asp.net POST action method?从 asp.net POST 操作方法更新休眠实体的正确方法是什么?
【发布时间】:2009-04-27 16:40:21
【问题描述】:

我是 nHibernate 的新手,并试图了解从 Web 应用程序表单 POST 更新分离对象的正确方法。 (我们使用的是 ASP.NET MVC)

我要更新的对象包含(除其他外)子对象的 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 已被默认模型绑定器从视图表单中抓取。

这似乎适用于以下任何情况:

  • 创建新的父对象
  • 修改父级的属性
  • 添加新的子对象
  • 修改现有子对象 (查看 nHibernate 日志,我可以看到它正确地确定对象是新的还是现有的,并发出适当的 UPDATE 或 INSERT)

失败的场景是: - 删除子对象 - 即如果它们不在 IList 中,它们不会从数据库中删除。没有例外或任何东西,它们只是不会被删除。

我的理解是,这是因为 nHibernate 执行创建需要删除的子列表的魔法不适用于分离实例。

我还没有找到一个简单的例子来说明这种动作方法在 nHibernate 中应该是什么样子(即使用模型绑定器对象作为分离的 nHibernate 实例)-基于 MS EF 的示例(例如http://stephenwalther.com/blog/archive/2009/02/27/chapter-5-understanding-models.aspx ) 似乎使用“ApplyPropertyChanges”方法将更改的属性从模型绑定对象复制到重新加载的实体实例。

所以,毕竟,问题很简单 - 如果我有模型绑定器给我一个包含子对象集合的新对象,我应该如何通过 nHibernate 更新它,(其中“更新”可能包括删除孩子)?

【问题讨论】:

    标签: asp.net-mvc nhibernate


    【解决方案1】:

    这是一个我认为您正在尝试做的示例。如果我误解了您要执行的操作,请告诉我。

    给定以下“域”类:

    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());
        }
    

    运行并生成以下 sql:

    DeletingChildrenOfEvictedObject.CanDeleteChildren:通过 NHibernate: 插入 [Person] (Name, Id) VALUES (@p0, @p1); @p0 = '乔',@p1 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

    NHibernate: INSERT INTO [Pet] (Name, Id) VALUES (@p0, @p1); @p0 = '狗',@p1 = '464e59c7-74d0-4317-9c22-9bf801013abb'

    NHibernate: INSERT INTO [Pet] (Name, Id) VALUES (@p0, @p1); @p0 = '猫',@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 = '狗',@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:从 [Pet] 中删除,其中 Id = @p0; @p0 = '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'

    注意 DELETE FROM [Pet]...

    所以,您需要做的是使用修改后的集合手动休眠一个 Person 对象(在此示例中),它应该能够确定要删除的内容。确保您设置了 Cascade.AllDeleteOrphan() 属性。

    【讨论】:

    • 非常感谢 - 有很多工作!不幸的是,我正在努力解决的一点是,我在第二个会话/事务(即我的 POST)中拥有的“Person”对象是由 ModelBinder 创建的全新对象,而不是具有修改了几个字段,并对其进行了一些“删除子”调用。我认为我正在寻找一种获取该新对象并将其更改应用于检索到的对象的方法,以便 nh 然后可以计算出所需的 SQL。也许那根本不存在。
    • 我倾向于处理这种情况的方法是将模型绑定器创建的新对象视为表示模型对象。您仍然需要检索要更新的对象(或以某种方式创建“持久”类的实例)并将这些更新应用于该对象。然后您可以将该对象保存到 NHibernate。这有意义吗?
    • 因此您将“手动”(即逐个属性,使用循环或子集合的任何内容),用“演示文稿”的属性覆盖“检索”对象的属性(即POST)'对象?并手动计算所需的子删除?似乎比我希望的要多一点工作(和维护!)。似乎几乎不值得为模型绑定器烦恼,真的,好像我必须一次处理一个字段,我不妨直接从表单响应中提取它们。不过,感谢您的帮助。
    【解决方案2】:

    Rob 的回答说服我更仔细地研究“将现有项目加载到新会话中然后合并”方法,当然还有 ISession.Merge,它似乎完全符合我的要求,即采取新对象并将其与刚刚重新加载到第二个会话的前任合并。

    所以我认为我试图问的问题的答案是“重新加载现有实体,然后用新实体调用'ISession.Merge'。”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-11
      • 1970-01-01
      • 2015-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多