NHibernate SaveOrUpdate 子集合未使用身份 ID 更新

发布于 2024-11-01 08:03:17 字数 2766 浏览 1 评论 0原文

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

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

简化的父映射

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

简化的服务方法步骤

然后我有一个服务方法来检索< em>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) 通过域对象方法将新的“子级”实体添加到“父级”,例如...

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 BeforeSendReply (IDispatchMessageInspector) 上提交的事务

 _session.Commit();

问题

通常,在保存实体时,在调用 SaveOrUpdate 之后,该实体的 Id 会反映 INSERT 语句生成的 Identity。但是,当我向现有父实体添加新的子实体时,Children 中的关联实体不会分配 Id。我需要将此 Id 传递回调用者,以便后续调用会导致更新,而不是额外的插入。

为什么 NHibernate 不更新底层实体? 我是否必须明确地对子项调用 SaveOrUpdate(如果是这种情况,那就很糟糕了)?

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

编辑 我应该注意到,新的子实体正在按预期保存;我只是拿不回分配的 ID。

更新 尝试按照下面 Michael Buen 的建议切换到 .Merge(~) ;导致 TransientObjectException 告诉我在调用合并之前显式保存修改后的子实体。我也尝试过 .Persist(~) 无济于事。非常感谢任何额外的见解。

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

I am clearly missing something (hopefully obvious), and I have had no luck with Google so far.

I have a Parent-Child relationship mapped as follows

Simplified Parent Map

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

Simplified Child Map

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

Simplified Service Method Steps

I then have a service method that retrieves Parent and adds a new Child via some domain method. The underlying NHibernate code boils down to:

1) Session Opened on WCF AfterReceiveRequest (IDispatchMessageInspector)

_sessionFactory.OpenSession();

2) Retrieve existing instance of parent via .Query

_session.Query<ParentEntity>()
        .Where(item => item.Id == parentId)
        .Fetch(item => item.SomeLookupColumn)
        .First();

3) Add new 'Child' entity to 'Parent' via domain object method such as...

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

  Children.Add(child);

  return child;
}

4) Ultimately calls SaveOrUpdate on 'Parent' entity.

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

Note: Use of SaveOrUpdate Does persist changes to database; use of Merge results in TransientObjectException mentioned below.

5) Finally, transaction committed on WCF BeforeSendReply (IDispatchMessageInspector)

 _session.Commit();

The Problem

Typically when an entity is saved, after the call to SaveOrUpdate, the Id of said entity refects the Identity generated by the INSERT statement. However, when I add a new child entity to an existing parent, the associated entity in Children does not get assigned an Id. I need to pass this Id back to the caller so subsequent calls result in UPDATES and not additional INSERTS.

Why is NHibernate not updating the underlying entity?
Do I explicitly have to call SaveOrUpdate on the child (that sucks if that is the case)?

Any thoughts on the matter greatly appreciated. Thanks!

EDIT
I should note, that the new child entity IS being saved as expected; I just don't get back the Id assigned.

UPDATE
Tried switching to .Merge(~) as suggested by Michael Buen below; resulting in a TransientObjectException telling me to explicitly save my modified child entity before calling merge. I also experimented with .Persist(~) to no avail. Any additional insight greatly appreciated.

object is an unsaved transient instance - save the transient instance before merging: My.NameSpace.ChildEntity

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蔚蓝源自深海 2024-11-08 08:03:30

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

You could call session.Flush(), which performs the inserts on the database. This is not ideal because of the impact to performance.

郁金香雨 2024-11-08 08:03:29

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

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

I finally found the time to dig around in the NHibernate code... the reason this is happening makes sense... short answer - the Cascade actions are processed on Session.Commit(); well after the above service operation has completed. Thus, my child objects haven't been refreshed to reflect the assigned ids by the time the service method returns the updated child entity.

Turns out I must call SaveOrUpdate directly on child entity if I need to know the Id immediately.

栀子花开つ 2024-11-08 08:03:29

对于分离场景,请使用合并(以前称为 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);

For detached scenario, use Merge(formerly known as SaveOrUpdateCopy)

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

Regarding:

Do I explicitly have to call
SaveOrUpdate on the child (that sucks
if that is the case)?

You don't need to, in fact that's where an ORM like NHibernate shines, it can save the whole object graph, and it really solves the impedance mismatch between the Object and database.

The physical implementation(for example, int or guid) of how a parent and child records(and sub-child records and so on) are linked is completely invisible from the app. You can concentrate well on your objects rather than the under-the-hood plumbing on what datatypes(int,guid,etc) your object relationships should use

[UPDATE]

You can still save the parent and the saving will cascade to children, and can get the child's id too. If you need to get the child id, get it after commit.

You cannot get the child id this way:

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

Must do this:

s.SaveOrUpdate(parentEntity);
tx.Commit();    
Console.WriteLine("{0}", parentEntity.ChildIdentity.First().ChildId);
诺曦 2024-11-08 08:03:29

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

Maybe this will help (It's about Inverse attribute):
http://www.emadashi.com/index.php/2008/08/nhibernate-inverse-attribute/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文