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

时间:2011-04-12 01:17:53

标签: c# .net 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();
    }
  }

简化的儿童地图

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

简化的服务方法步骤

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

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

_sessionFactory.OpenSession();

2)通过.Query

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

3)通过域对象方法(如...

)将新的“Child”实体添加到“Parent”
public ChildEntity CreateChild()
{
  var child = new ChildEntity{ Parent = this };

  Children.Add(child);

  return child;
}

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

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

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

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

 _session.Commit();

问题

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

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

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

修改 我应该注意,新的子实体是按预期保存的;我只是不回复Id分配。

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

  

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

4 个答案:

答案 0 :(得分:13)

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

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

答案 1 :(得分: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();

必须这样做:

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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