NHibernate SaveOrUpdate子Collection未更新Identity id

本文关键字:更新 Identity id Collection SaveOrUpdate NHibernate | 更新日期: 2023-09-27 17:50:08

我显然错过了一些东西(希望是显而易见的),到目前为止我在谷歌上没有运气。

我有一个父子关系映射如下

简化父映射

  public sealed class ParentMap : ClassMap<ParentEntity>
  {
    public ParentMap()
    {
      Table("Parent");
      Component(x => x.Thumbprint);
      Id(x => x.Id).GeneratedBy.Identity();
      Map(x => x.ServeralNotNullableProperties).Not.Nullable();
      HasMany(x => x.Children).KeyColumn("ChildId")
                              .Inverse()
                              .LazyLoad()
                              .Cascade
                              .AllDeleteOrphan();
      References(x => x.SomeUnrelatedLookupColumn).Column("LookupColumnId").Not.Nullable();
    }
  }

简化子地图

  public sealed class ChildMap : ClassMap<ChildEntity>
  {
    public ChildMap()
    {
      Table("Child");
      Component(x => x.Thumbprint);
      Id(x => x.Id).GeneratedBy.Identity();
      Map(x => x.MoreNotNullableProperties).Not.Nullable();
      References(x => x.Parent).Column("ParentId").Not.Nullable();
    }
  }

简化业务方法步骤

然后,我有一个服务方法来检索,并通过一些域方法添加一个新的。底层的NHibernate代码可以归结为:

1)在WCF上打开的会话

_sessionFactory.OpenSession();

2)通过.Query

检索父节点的现有实例
_session.Query<ParentEntity>()
        .Where(item => item.Id == parentId)
        .Fetch(item => item.SomeLookupColumn)
        .First();

3)通过域对象方法将新的"子"实体添加到"父"实体中,如…

public ChildEntity CreateChild()
{
  var child = new ChildEntity{ Parent = this };
  Children.Add(child);
  return child;
}

4)最终在"父"实体上调用SaveOrUpdate。

 // ChildEntity Id is 0
 _session.SaveOrUpdate(parentEntity)
 // Expect ChildEntity Id to NOT be 0

注意:使用SaveOrUpdate 是否将更改持久化到数据库;使用Merge会导致下面提到的TransientObjectException。

5)最后,WCF上提交的事务beforeendreply (IDispatchMessageInspector)

 _session.Commit();

通常在保存实体时,在调用SaveOrUpdate之后,所述实体的Id反映由INSERT语句生成的Identity。但是,当我向现有父实体添加新的子实体时,Children中的关联实体没有被分配Id。我需要将此Id传递回调用者,以便后续调用产生UPDATES,而不是额外的insert。

为什么NHibernate不更新底层实体?我是否明确地调用SaveOrUpdate的孩子(如果是这种情况下,这很糟糕)?

对于这件事的任何想法,我都非常感激。谢谢!

编辑我应该注意到,新的子实体正在按预期保存;我只是没有得到分配的Id。

尝试切换到。merge(~),正如下面Michael Buen建议的那样;导致TransientObjectException,告诉我在调用merge之前显式保存修改过的子实体。我也尝试过……坚持(~),但无济于事。如有任何额外的见解,不胜感激。

对象是一个未保存的瞬态实例-合并前保存瞬态实例:

NHibernate SaveOrUpdate子Collection未更新Identity id

我终于找到时间在NHibernate代码中挖掘了一下…发生这种情况的原因是有道理的……简短的回答-级联操作是在Session.Commit()上处理的;待上述服务操作完成后。因此,在服务方法返回更新后的子实体之前,我的子对象还没有被刷新以反映分配的id。

事实证明,如果我需要立即知道Id,我必须直接在子实体上调用SaveOrUpdate。

对于分离场景,使用Merge(以前称为SaveOrUpdateCopy)

示例:http://www.ienablemuch.com/2011/01/nhibernate-saves-your-whole-object.html

关于:

我是否必须明确地调用对子节点进行SaveOrUpdate操作(这太糟糕了如果是这样的话)?

你不需要,事实上这就是像NHibernate这样的ORM的亮点所在,它可以保存整个对象图,并且它真正解决了对象和数据库之间的阻抗不匹配。

父记录和子记录(以及子记录等)如何链接的物理实现(例如,int或guid)在应用程序中是完全不可见的。你可以很好地专注于你的对象,而不是在你的对象关系应该使用什么数据类型(int,guid等)的底层管道

(更新)

你仍然可以保存父级,并且保存将级联到子级,并且也可以获得子级的id。如果需要获取子id,请在提交后获取。

你不能通过这种方式获得子id:

s.SaveOrUpdate(parentEntity);
Console.WriteLine("{0}", parentEntity.ChildIdentity.First().ChildId);
tx.Commit();

必须这样做:

s.SaveOrUpdate(parentEntity);
tx.Commit();    
Console.WriteLine("{0}", parentEntity.ChildIdentity.First().ChildId);

也许这将有助于(这是关于逆属性):http://www.emadashi.com/index.php/2008/08/nhibernate-inverse-attribute/

您可以调用session.Flush(),它在数据库上执行插入操作。这并不理想,因为这会影响性能。