NHibernate SaveOrUpdate子集合未使用Identity ID更新

Chr*_*ter 10 .net c# nhibernate fluent-nhibernate

我显然遗漏了一些东西(希望很明显),到目前为止我还没跟谷歌好运.

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

简化的父图

  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();
    }
  }
Run Code Online (Sandbox Code Playgroud)

简化的儿童地图

  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();
    }
  }
Run Code Online (Sandbox Code Playgroud)

简化的服务方法步骤

然后我有一个服务方法,它检索Parent并通过一些域方法添加一个新的Child.底层的NHibernate代码归结为:

1)在WCF AfterReceiveRequest(IDispatchMessageInspector)上打开的会话

_sessionFactory.OpenSession();
Run Code Online (Sandbox Code Playgroud)

2)通过.Query检索父项的现有实例

_session.Query<ParentEntity>()
        .Where(item => item.Id == parentId)
        .Fetch(item => item.SomeLookupColumn)
        .First();
Run Code Online (Sandbox Code Playgroud)

3)通过域对象方法将新的'Child'实体添加到'Parent',例如......

public ChildEntity CreateChild()
{
  var child = new ChildEntity{ Parent = this };

  Children.Add(child);

  return child;
}
Run Code Online (Sandbox Code Playgroud)

4)最终在'Parent'实体上调用SaveOrUpdate.

 // ChildEntity Id is 0
 _session.SaveOrUpdate(parentEntity)
 // Expect ChildEntity Id to NOT be 0
Run Code Online (Sandbox Code Playgroud)

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

5)最后,在WCF BeforeSendReply上提交事务(IDispatchMessageInspector)

 _session.Commit();
Run Code Online (Sandbox Code Playgroud)

问题

通常,在保存实体时,在调用SaveOrUpdate之后,所述实体的Id会反映INSERT语句生成的Identity.但是,当我向现有父级添加新的子实体时,关联的实体Children不会被分配Id.我需要将此Id传递给调用者,以便后续调用导致UPDATES而不是其他INSERTS.

为什么NHibernate不更新底层实体?我是否明确要在孩子身上调用SaveOrUpdate(如果是这样的话会很糟糕)?

对此事的任何想法都非常感激.谢谢!

编辑 我应该注意,新的子实体是按预期保存的; 我只是不回复ID分配.

更新 尝试切换到.Merge(〜),如Michael Buen所示; 导致TransientObjectException告诉我在调用merge之前显式保存我修改的子实体.我也试过.Persist(〜)无济于事.任何额外的见解非常感谢.

object是未保存的瞬态实例 - 在合并之前保存瞬态实例:My.NameSpace.ChildEntity

Chr*_*ter 13

我终于找到了在NHibernate代码中挖掘的时间......这种情况发生的原因是有道理的......简短回答 - Cascade动作在Session.Commit()上处理; 在上述服务操作完成后.因此,在服务方法返回更新的子实体时,我的子对象尚未刷新以反映分配的ID.

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


Mic*_*uen 5

对于分离方案,请使用Merge(以前称为SaveOrUpdateCopy)

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

关于:

我是否明确要在孩子身上调用SaveOrUpdate(如果是这样的话会很糟糕)?

事实上,你不需要像NHibernate这样的ORM,它可以保存整个对象图,它确实解决了对象和数据库之间的阻抗不匹配问题.

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

[UPDATE]

您仍然可以保存父级,并且保存将级联到子级,并且也可以获取子级的ID.如果您需要获取子ID,请在提交后获取它.

你不能这样得到孩子的身份:

s.SaveOrUpdate(parentEntity);
Console.WriteLine("{0}", parentEntity.ChildIdentity.First().ChildId);
tx.Commit();
Run Code Online (Sandbox Code Playgroud)

必须这样做:

s.SaveOrUpdate(parentEntity);
tx.Commit();    
Console.WriteLine("{0}", parentEntity.ChildIdentity.First().ChildId);
Run Code Online (Sandbox Code Playgroud)