流利的 nHIbernate 列与基类上的版本映射 - 保存子项导致“对象引用未保存的瞬态实例.“错误

本文关键字:保存 对象引用 错误 实例 基类 版本 映射 nHIbernate | 更新日期: 2023-09-27 17:56:10

我的情况是更新日期列映射为:

public abstract class AuditableEntityMapBase<T> : ClassMap<T>
{
    protected AuditableEntityMapBase()
    {
        ...
        OptimisticLock.Version();
        Version(x => (x as AuditableEntityBase).UpdateDT).Nullable();
        ...
    }
}

在父级(个人实体)和子级(具有Cascade.None for Person)继承的AuditableEntityMapBase基类上,映射如下:

public class PersonTelephoneMap : AuditableEntityMapBase<PersonTelephone>
{
    public PersonTelephoneMap()
    {
        Table("pers_PersonTelephone");
        Id(x => x.Id, "PersonTelephoneId");
        References(x => x.Person, "PersonId")
            .Cascade.None();
        ...
    }
}

public class PersonMap : AuditableEntityMapBase<Person>
{
    public PersonMap()
    {
        Table("pers_Person");
        Id(x => x.Id, "PersonId"); //.Unique().GeneratedBy.Native();
        ...
        HasMany(x => x.Phones)
            .KeyColumn("PersonId")
            .Inverse()
            .Cascade.All();
        ...
    }
}

保存子项和刷新会话(如以下测试所示)会导致 Parent 上的"对象引用未保存的瞬态实例 - 在刷新前保存瞬态实例":

/// <summary>
/// Tests nHibernate for concurrency (dirty read)
/// 1. Telephone1 and Telephone2 entities are loaded in separate sessions
/// 2. Telephone1 is updated - Telephone2 now has a dirty read
/// 3. Update Telephone2 and expect NHibernate.StaleObjectStateException error
/// </summary>
[Test]
[ExpectedException("NHibernate.StaleObjectStateException")] //Assert
public void CanVersionConcurrencyPersonTelephone()
{
    //Arrange
    const string telNo1 = "911";
    const string telNo2 = "999";            
    Person person2 = null;
    PersonTelephone personTelephone2 = null;
    var person = CreatePerson(); //Create a new person entity            
    var personManager = new PersonManager();           
    //Act
    //var person1 = personManager.Read(person.Id);
    var personTelephone1 = person.Phones[0];
    SessionContext.Current.AttachEntity(personTelephone1);
    SessionContext.Flush();
    using (SessionContext.Open())
    {
        person2 = personManager.Read(person.Id);
        personTelephone2 = person2.Phones[0];
        SessionContext.Flush();
    }
    System.Threading.Thread.Sleep(2000); //Arrange for a dirty read by user delay 
    using (SessionContext.Open())
    {
        personTelephone1.Number = telNo1;
        personManager.UpdateTelephone(personTelephone1); //simulate dirty read for personTelephone2
        SessionContext.Flush();
    }
    using (SessionContext.Open())
    {
        personTelephone2.Number = telNo2;
        personManager.UpdateTelephone(personTelephone2); //expect NHibernate.StaleObjectStateException
        SessionContext.Flush();
    }
}

对我来说,在 PersonTelephone 映射上使用 Cascade.SaveUpdate 而不是 Cascade.SaveUpdate 进行 nHibernate 更新是不可行的,如下所示:

 References(x => x.Person, "PersonId")
                .Cascade.SaveUpdate();

我还尝试使用ReadOnly方法,该方法最初有效:

References(x => x.Person, "PersonId")
                    .Cascade.None.ReadOnly();

但是,这导致了我插入 PersonTelephone 表的问题,因为 PersonId 是一个非空列,并且在 nHibernate 插入期间没有注入,因为它是只读的。

悲观锁定不符合我的用户要求,OptimisticLock.All() 的性能受到影响。我也尝试使用.Cascade.None() 在 Person 实体映射上。

唯一有效的方法是在"人员"和"人员电话"表上有一个唯一的"更新"字段。这种解决方案对我来说很臭。然后我尝试为 nHibernate 实体字段提供唯一名称,但不起作用。还有其他人遇到过这种情况吗?有没有优雅的解决方案?

流利的 nHIbernate 列与基类上的版本映射 - 保存子项导致“对象引用未保存的瞬态实例.“错误

您已将"个人"记录的"所有权"颠倒过来;PersonPhones"拥有"他们对Person的引用。但是,级联关系是自上而下的;人员电话在他们的人是时保存,但不是相反。

所以,你得到的是你有一个新的人,一个新的PersonPhone,你正在保存PersonPhone。该人尚未存在于数据库中,并且NH被告知在保存PersonPhone时不要保存该人,因此它唯一能做的就是抱怨。

要解决此问题,请保存人员,而不是人员电话。这将插入或更新人员,然后级联以插入或更新人员电话,包括您的新电话。