为什么当行版本不匹配时,nHibernate不会在SaveOrUpdate上抛出陈旧状态异常?

时间:2010-10-27 10:31:29

标签: nhibernate

这会导致StaleStateException异常:

var entity = Session.Load(id);
Session.Evict(entity);
entity.SomePropert = "Something";
entity.Version = Version--;
Session.SaveOrUpdate(entity); // Throws Exception

然而,这不会引发异常。也没有更新行。

var entity = Session.Load(id);
entity.SomePropert = "Something";
entity.Version = Version--;
Session.SaveOrUpdate(entity); // Does not throw Exception, 
                              // but no rows are updated

1 个答案:

答案 0 :(得分:5)

简短的回答是因为版本是由NHibernate管理的属性。您可以更改它,但忽略您的更改。正如您所发现的,改变操作的一种方法是逐出对象,然后重新附加/保存/更新。驱逐导致NHibernate忘记它的版本值是什么让它从对象中获取它。 (我在这里可能过于简单了。)

此问题之前已经提出过。很多人都会问为什么有人会这样做 - 一个有效的场景是无状态的Web应用程序。每个HTTP请求都会创建自己的NHibernate会话,该请求在请求完成时会死亡。可以请求页面以显示要编辑的对象的部分细节。 ID和版本保存在隐藏字段中。在postpack(这是一个新请求,这次仅使用数据),新的NHibernate会话重新加载有问题的对象,应用表单数据(包括版本)中的更改,然后保存对象。在此方案中修改此处的版本是完全有效的。如果其他人在初始请求和回发之间修改了相同的对象,则不应覆盖这些更改。应抛出过时的对象异常,但不会抛出,因为NHibernate会忽略版本更改。

我知道有两种方法可以修复这些场景的版本“bug”。一个是OP已经说明的驱逐方法。第二种是创建一个NHibernate拦截器来捕获更新操作并手动检查版本。

这里讨论了这个问题和两个解决方案。

https://forum.hibernate.org/viewtopic.php?t=977889

我使用从此处提供的代码修改的拦截器。

http://weblogs.asp.net/stefansedich/archive/2008/10/01/set-the-value-of-a-version-column-in-nhibernate-manually.aspx

我的拦截器的内容看起来略有不同(我使用NHibernate 2.1.2.4000)。

private void CheckVersion(object domainObject, object uid)
{
    ISessionImplementor sessionImpl = Session.GetSessionImplementation();
    IEntityPersister persister = sessionImpl.GetEntityPersister(null, domainObject);

    if (persister.IsVersioned)
    {
        EntityMode mode = Session.GetSessionImplementation().EntityMode;
        object version = persister.GetVersion(domainObject, mode);
        object currentVersion = persister.GetCurrentVersion(uid, sessionImpl);

        if (!persister.VersionType.IsEqual(version, currentVersion))
            throw new StaleObjectStateException(persister.EntityName, uid);
    }
}