首页 文章

在基类上使用版本映射的Fluent nHIbernate列 - 保存Child导致父对象上的“对象引用未保存的瞬态实例...”错误

提问于
浏览
0

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

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

在AuditableEntityMapBase基类上,Parent(Person实体)和Child(具有Cascade.None for Person的PersonTelephone实体)继承映射如下:

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();

        ...
    }
}

在以下测试中保存Child和刷新会话会导致“对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例”:

/// <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进行水合Person实体和nHibernate更新是不可行的,如下所示:

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

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

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

但是,这导致我插入PersonTelephone表时出现问题,因为PersonId是一个Not Null列,并且在nHibernate插入期间没有注入,因为它是只读的 .

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

唯一有效的方法是在Person和PersonTelephone表上有一个唯一的Update字段 . 这个解决方案闻到了我的味道 . 然后我试图给nHibernate实体字段赋予唯一的名称,但是没有用 . 还有其他人遇到过这个吗?有优雅的解决方案吗?

1 回答

  • 0

    您对人员记录的“所有权”进行了撤销; PersonTelephones“拥有”他们对Person的引用 . 然而,级联关系是自上而下的;人员电话是在他们的人员保存时保存的,但不是相反 .

    所以,你得到的是你有一个新人,他有一个新的PersonTelephone,你正在保存PersonTelephone . 数据库中尚不存在Person,并且当PersonTelephone被保存时,NH被告知不要保存Person,所以它唯一能做的就是抱怨 .

    要解决此问题,请保存Person,而不是PersonTelephone . 这将插入或更新Person,然后级联以插入或更新PersonTelephones,包括您的新PersonTelephones .

相关问题