我的项目有一个Ticket
实体,其OwnedBy
属性。我正在使用nHibernate将票证持久保存到数据库中。
潜在故障单所有者的规范来源是Active Directory。由于我不想每次加载票证时都要查询Active Directory,因此我还将Ticket.OwnedBy
保留到数据库并在获取时从加载票。
重新分配故障单的所有者后,我从Active Directory获取新的Owner
并将其分配给Ticket.OwnedBy
,然后调用Session.SaveOrUpdate(故障单)。当我提交事务时,NHibernate会抛出NonUniqueObjectException
,因为具有相同ID的Owner
已经与会话关联。
class Ticket {
public int Id { get; set; }
public Owner OwnedBy { get; set; }
/* other properties, etc */
}
class Owner {
public Guid Guid { get; set; }
public string Name { get; set; }
public string Email { get; set; }
/* other properties, etc */
}
class TicketMap : ClassMap<Ticket> {
public TicketMap() {
Id(x => x.Id);
References(x => x.OwnedBy)
.Cascade.SaveUpdate()
.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);
/* other properties, etc */
}
}
// 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
我希望nHibernate使用未附加的属性覆盖现有Owner
的属性。 (我从AD获取的对象中的名称或电子邮件可能不同,AD应该是规范来源。)我尝试在SaveOrUpdate(票证)之前调用Session.SaveOrUpdateCopy(ticket.OwnedBy)
和Session.Merge(ticket.OwnedBy)
,但是异常仍然被抛出。我还阅读了关于NonUniqueObjectException
的{{3}},但调用Session.Lock()也不起作用。
我有两个问题:
答案 0 :(得分:2)
合并工作,最可能的问题是你没有正确调用它。 Merge将使用新对象属性更新现有对象,但Merge不会附加新对象。所以你必须使用现有的。如果在合并后使用新对象,则仍会出现相同的错误。
以下代码应解决问题:
//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();
Get会话从会话中检索到的所有者将拥有newOwner属性,但对会话也有效。
答案 1 :(得分:0)
为了保持现有Owner-entity的分离实例,只需在Owner实例上调用merge而不调用SaveOrUpdate就足够了。然后它将插入实体或更新现有实体。
如果合并不起作用,则出现问题。在这种情况下发布更多代码并发布您的映射。
顺便说一下:你也坚持门票吗?如果是这样,映射似乎很奇怪。你应该在Ticket和Map OwnedBy上有一个唯一的ID作为参考,可能是一个带有级联的逆映射。更新: 你应该从两侧绘制它。使用Cascade和反向映射将所有者身份作为HasMany映射到故障单。将故障单侧映射为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 */
}
这应该很好用。