我可以强制 nHibernate 保留一个与已加载实体具有相同 ID 的新实体吗?
我的项目有一个带有 OwnedBy
属性的 Ticket
实体。我正在使用 nHibernate 将票证保存到数据库中。
潜在票证所有者的规范来源是 Active Directory。由于我不想每次加载票证时都必须查询 Active Directory,因此我也将 Ticket.OwnedBy
保存到数据库并从那里加载< /em> 取票时。
重新分配票证所有者后,我从 Active Directory 获取新的 Owner
并将其分配给 Ticket.OwnedBy
,然后调用 Session.SaveOrUpdate(ticket)。当我提交事务时,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 */
}
流畅的 nHibernate 映射
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 应该是规范源。)我尝试调用 Session.SaveOrUpdateCopy(ticket.OwnedBy)
和 Session.Merge(ticket.OwnedBy)
在 SaveOrUpdate(ticket) 之前,但仍然抛出异常。我还阅读了关于 NonUniqueObjectException
的这个相关问题,但是调用Session.Lock() 也不起作用。
我有两个问题:
- 有没有一种简单的方法可以让 nHibernate 屈服于我的意愿?
- 我可能在尝试将从 AD 获取的所有者视为与我存储在数据库中的所有者相同的类型时犯了架构错误。我怎样才能改进这个设计,这样我就不需要让 nHibernate 屈服于我的意愿?
My project has a Ticket
entity with an OwnedBy
property. I'm using nHibernate to persist the tickets to a database.
The canonical source for potential ticket owners is Active Directory. Since I don't want to have to query Active Directory every time I load tickets, I also persist Ticket.OwnedBy
to the database and load it from there when fetching tickets.
When a ticket's owner is reassigned, I get the new Owner
from Active Directory and assign it to Ticket.OwnedBy
, then call Session.SaveOrUpdate(ticket). When I commit the transaction, NHibernate throws a NonUniqueObjectException
because an Owner
with the same ID is already associated with the session.
Class definitions
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 */
}
Fluent nHibernate Mappings
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 */
}
}
Sample code
// 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
I want nHibernate to overwrite the properties of the existing Owner
with the properties of the unattached one. (The Name or Email in the object I fetched from AD may be different, and AD is supposed to be the canonical source.) I've tried calling Session.SaveOrUpdateCopy(ticket.OwnedBy)
and Session.Merge(ticket.OwnedBy)
before the SaveOrUpdate(ticket), but the exception is still being thrown. I've also read this related question about NonUniqueObjectException
, but calling Session.Lock() didn't work either.
I have two questions:
- Is there an easy way to bend nHibernate to my will?
- I may have made an architectural misstep in trying to treat the owners I fetch from AD as the same type as the owners I store in the DB. How can I improve this design so I won't need to bend nHibernate to my will?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
合并有效,最可能的问题是您没有正确调用它。合并将使用新对象属性更新现有对象,但合并不会附加新对象。所以你必须使用现有的。如果您在合并后使用新对象,您仍然会遇到相同的错误。
以下代码应该可以解决该问题:
通过 Get 从会话中检索的 Owner 将具有 newOwner 属性,但对会话也有效。
Merge works, the most likely issue is you did not call it properly. Merge will update the existing object with the new object properties but Merge does not attach the new object. So you have to use the existing one. If you use the new object after a merge you still get the same error.
The following code should fix the problem:
The Owner retrieved from the session by Get will have the newOwner properties but also be valid for the session.
为了持久保存现有所有者实体的分离实例,在所有者实例上调用 merge 应该足够,而不需要调用 SaveOrUpdate。然后它将插入实体或更新现有实体。
如果合并不起作用,则说明有问题。在这种情况下发布更多代码并发布您的映射。
顺便说一句:你也保留门票吗?如果是这样,那么映射就显得相当奇怪了。您应该在 Ticket 和 Map OwnedBy 上有一个唯一的 ID 作为参考,可能作为带有级联的逆映射。
更新:
您应该从两侧绘制它。使用级联和逆映射将所有者端映射为 HasMany 到 Tickets。将 Ticket 端映射为 Cascade.None() 并作为引用。
那应该很好用。
In order to persist the detached instance of an existing Owner-entity, it should be enough to call merge on the Owner instance without the call to SaveOrUpdate. It will then either insert the entity or update the existing one.
If merge does not work, then something is wrong. Post more code in that case and post your mapping.
BTW: Do you persist the Tickets, too? If so, the mapping seems rather odd. You should have a unique ID on Ticket and Map OwnedBy as a Reference, probably as an inverse mapping with a cascade on it.
Update:
You should map it from both sides. Map the owner-side as a HasMany to Tickets with your Cascade and as Inverse Mapping. Map the Ticket side as Cascade.None() and as a Reference.
That should work nicely.