2

我的项目有一个具有属性的Ticket实体。OwnedBy我正在使用 nHibernate 将票证保存到数据库中。

潜在票证所有者的规范来源是 Active Directory。由于我不想每次加载票证时都查询 Active Directory,因此我保留Ticket.OwnedBy到数据库并在获取票证时从那里加载它。

当票证的所有者被重新分配时,我Owner从 Active Directory 中获取新的并将其分配给Ticket.OwnedBy,然后调用 Session.SaveOrUpdate(ticket)。当我提交事务时,NHibernate 会抛出一个,NonUniqueObjectException因为Owner具有相同 ID 的一个已经与会话相关联。

类定义

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 应该是规范来源。)我尝试在 SaveOrUpdate(ticket) 之前调用Session.SaveOrUpdateCopy(ticket.OwnedBy)Session.Merge(ticket.OwnedBy),但仍然抛出异常。我也读过这个相关的问题NonUniqueObjectException但是调用 Session.Lock() 也没有用。

我有两个问题:

  1. 有没有一种简单的方法可以让 nHibernate 服从我的意愿?
  2. 在尝试将我从 AD 获取的所有者视为与我存储在数据库中的所有者相同的类型时,我可能在架构上犯了错误。我怎样才能改进这个设计,这样我就不需要让 nHibernate 屈服于我的意愿了?
4

2 回答 2

2

合并有效,最可能的问题是您没有正确调用它。合并将使用新对象属性更新现有对象,但合并不会附加新对象。所以你必须使用现有的。如果您在合并后使用新对象,您仍然会收到相同的错误。

下面的代码应该可以解决这个问题:

//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 从会话中检索的 Owner 将具有 newOwner 属性,但对会话也有效。

于 2010-11-28T21:05:04.333 回答
0

为了保持现有所有者实体的分离实例,在所有者实例上调用合并就足够了,而无需调用 SaveOrUpdate。然后它将插入实体或更新现有实体。

如果合并不起作用,则说明有问题。在这种情况下发布更多代码并发布您的映射。

顺便说一句:你也坚持门票吗?如果是这样,映射似乎很奇怪。您应该在 Ticket 和 Map OwnedBy 上有一个唯一 ID 作为参考,可能是一个带有级联的逆映射。

更新:你应该从两边映射它。使用您的级联和反向映射将所有者端作为 HasMany 映射到工单。将 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 */
}

那应该很好用。

于 2010-11-24T20:36:06.670 回答