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.
对于分离方案,请使用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)