我是否可以强制nHibernate保留一个新实体,该实体的ID与已加载的实体相同?
我的项目有一个我是否可以强制nHibernate保留一个新实体,该实体的ID与已加载的实体相同?,nhibernate,object-model,Nhibernate,Object Model,我的项目有一个票证实体,该实体具有一个所有者属性。我正在使用nHibernate将票据持久化到数据库 潜在票证所有者的规范源是Active Directory。由于我不希望每次加载票证时都必须查询Active Directory,因此我还将Ticket.OwnedBy持久化到数据库中,并在获取票证时从那里加载它 重新分配票证的所有者时,我从Active Directory获取新的所有者,并将其分配给ticket.OwnedBy,然后调用Session.SaveOrUpdate(票证)。当我提交事
票证
实体,该实体具有一个所有者
属性。我正在使用nHibernate将票据持久化到数据库
潜在票证所有者的规范源是Active Directory。由于我不希望每次加载票证时都必须查询Active Directory,因此我还将Ticket.OwnedBy
持久化到数据库中,并在获取票证时从那里加载它
重新分配票证的所有者时,我从Active Directory获取新的所有者
,并将其分配给ticket.OwnedBy
,然后调用Session.SaveOrUpdate(票证)。当我提交事务时,NHibernate抛出一个unonqueObjectException
,因为具有相同ID的所有者已与会话关联
类定义
Fluent-nHibernate映射
classticketmap:ClassMap{
公开票证地图(){
Id(x=>x.Id);
引用(x=>x.OwnedBy)
.Cascade.SaveUpdate()
.Not.Nullable();
/*其他财产等*/
}
}
类所有者映射:类映射{
公共所有者映射(){
Id(x=>x.Guid)
.GeneratedBy.Assigned()
Map(x=>x.Name);
Map(x=>x.Email);
/*其他财产等*/
}
}
示例代码
//unitOfWork.Session是NHibernate.ISession的一个实例
票证=unitOfWork.Session.Get(1);
所有者newOwner=activeDirectoryRepo.FindByGuid(/*新所有者的guid,来自用户*/);
ticket.OwnedBy=新所有者;
unitOfWork.Session.SaveOrUpdate(票证);
unitOfWork.Commit();//抛出非请求对象异常
我希望nHibernate用未附加所有者的属性覆盖现有所有者的属性。(我从AD获取的对象中的名称或电子邮件可能不同,AD应该是规范源。)我尝试在SaveOrUpdate(ticket)之前调用Session.SaveOrUpdateCopy(ticket.OwnedBy)
和Session.Merge(ticket.OwnedBy)
,但仍会引发异常。我也读过关于unonqueObjectException
,但是调用Session.Lock()也不起作用
我有两个问题:
有没有一种简单的方法可以让nHibernate服从我的意愿
我可能在试图将我从广告中获取的所有者与我存储在DB中的所有者视为同一类型时犯了架构错误。我怎样才能改进这个设计,这样我就不需要按照自己的意愿弯曲nHibernate了
为了持久化现有所有者实体的分离实例,在不调用SaveOrUpdate的情况下对所有者实例调用merge就足够了。然后,它将插入实体或更新现有实体
如果合并不起作用,那么就有问题了。在这种情况下发布更多代码并发布映射
顺便问一下:你也坚持买票吗?如果是这样的话,这种映射似乎相当奇怪。您应该在Ticket和Map OwnedBy上有一个唯一的ID作为引用,可能是一个带有级联的反向映射
更新:
你应该从两边把它画出来。将所有者端映射为带有级联的HasMany-to-ticket和反向映射。将票证端映射为Cascade.None()并作为引用
public TicketMap() {
Id(x => x.Id);
References(x => x.OwnedBy)
.Cascade.None()
.Not.Nullable();
/* other properties, etc */
}
class OwnerMap : ClassMap<Owner> {
public OwnerMap() {
Id(x => x.Guid)
.GeneratedBy.Assigned()
Map(x => x.Name);
Map(x => x.Email);
HasMany<Ticket>(x => x.Tickets).KeyColumn("TicketId").Cascade.AllDeleteOrphan().LazyLoad().Inverse().NotFound.Ignore();
/* other properties, etc */
}
public TicketMap(){
Id(x=>x.Id);
引用(x=>x.OwnedBy)
.Cascade.None()
.Not.Nullable();
/*其他财产等*/
}
类所有者映射:类映射{
公共所有者映射(){
Id(x=>x.Guid)
.GeneratedBy.Assigned()
Map(x=>x.Name);
Map(x=>x.Email);
HasMany(x=>x.Tickets).KeyColumn(“TicketId”).Cascade.AllDeleteOrphan().LazyLoad().Inverse().NotFound.Ignore();
/*其他财产等*/
}
这应该可以很好地工作。合并工作正常,最可能的问题是您没有正确调用它。合并将使用新对象属性更新现有对象,但合并不会附加新对象。因此,您必须使用现有对象。如果在合并后使用新对象,您仍然会收到相同的错误
以下代码应修复此问题:
//Merge the new Owner
unitOfWork.Session.Merge(newOwner);
//Get a valid Owner by retrieving from the session
Owner owner = session.Get<Owner>(newOwner.Id);
// Set the ticket to this owner instance instead of the new one
ticket.OwnedBy = owner;
unitOfWork.Session.Update(ticket);
unitOfWork.Commit();
//合并新所有者
unitOfWork.Session.Merge(新所有者);
//通过从会话检索来获取有效的所有者
所有者=session.Get(newOwner.Id);
//将票证设置为此所有者实例而不是新实例
ticket.OwnedBy=所有者;
unitOfWork.Session.Update(票据);
unitOfWork.Commit();
Get从会话中检索到的所有者将具有newOwner属性,但也对会话有效。假设SecondOwner是新所有者,那么您为什么不能只执行Ticket.OwnedBy=SecondOwner
,然后调用SaveOrUpdate(Ticket)?也许你应该实现一个IInterceptor
?你能展示一下你在保存票据时如何替换所有者的代码吗?映射也会有帮助。@Will,@James:我用映射和我用来更改所有者的代码更新了帖子。是的,票据是根域实体。我不会直接保留所有者,只会通过其他域实体tities喜欢Ticket。我也用映射和更多的代码更新了这个问题。在这种情况下,从两边映射是解决方案吗?所有者目前没有Tickets
集合,我的应用程序也没有任何真正的需要。我承认我对NHibernate没有很好的理解,但这到底会是什么是吗?您想要一个双向映射。从逻辑上看,所有者有许多票证。票证只包含对所有者的引用,但不包含一个。我相信Merge将返回一个合并的对象实例,这样您就可以执行Owner Owner=unitOfWork.Session.Merge(newOwner);Good call,为您保存get语句。您必须强制转换对象,并且应该可以:ticket.OwnedBy=unitOfWork.Session.Merge(newOwner)作为所有者在我的问题中,我说我尝试了这个,并且仍然得到了异常,但在仔细查看代码后,我发现
// unitOfWork.Session is an instance of NHibernate.ISession
Ticket ticket = unitOfWork.Session.Get<Ticket>(1);
Owner newOwner = activeDirectoryRepo.FindByGuid(/* guid of new owner, from user */);
ticket.OwnedBy = newOwner;
unitOfWork.Session.SaveOrUpdate(ticket);
unitOfWork.Commit(); // Throws NonUniqueObjectException
public TicketMap() {
Id(x => x.Id);
References(x => x.OwnedBy)
.Cascade.None()
.Not.Nullable();
/* other properties, etc */
}
class OwnerMap : ClassMap<Owner> {
public OwnerMap() {
Id(x => x.Guid)
.GeneratedBy.Assigned()
Map(x => x.Name);
Map(x => x.Email);
HasMany<Ticket>(x => x.Tickets).KeyColumn("TicketId").Cascade.AllDeleteOrphan().LazyLoad().Inverse().NotFound.Ignore();
/* other properties, etc */
}
//Merge the new Owner
unitOfWork.Session.Merge(newOwner);
//Get a valid Owner by retrieving from the session
Owner owner = session.Get<Owner>(newOwner.Id);
// Set the ticket to this owner instance instead of the new one
ticket.OwnedBy = owner;
unitOfWork.Session.Update(ticket);
unitOfWork.Commit();