NHibernate-处理StaleObjectStateException以始终提交客户端更改-需要建议/建议

NHibernate-处理StaleObjectStateException以始终提交客户端更改-需要建议/建议,nhibernate,concurrency,merge,Nhibernate,Concurrency,Merge,我试图找到处理此异常的完美方法,并强制客户端更改覆盖导致冲突的任何其他更改。我提出的方法是将对Session.Transaction.Commit()的调用包装在一个循环中,在循环中我将执行一个try-catch块,并通过复制每个过时对象的属性来分别处理它,除行版本属性外,然后刷新对象以获取最新的DB数据,然后将原始值重新复制到刷新的对象,然后执行合并。循环后,我将提交,如果发生任何其他StaleObjectStateException,则同样适用。循环将一直循环,直到所有冲突都得到解决 此方法

我试图找到处理此异常的完美方法,并强制客户端更改覆盖导致冲突的任何其他更改。我提出的方法是将对
Session.Transaction.Commit()
的调用包装在一个循环中,在循环中我将执行一个try-catch块,并通过复制每个过时对象的属性来分别处理它,除行版本属性外,然后刷新对象以获取最新的DB数据,然后将原始值重新复制到刷新的对象,然后执行合并。循环后,我将提交,如果发生任何其他
StaleObjectStateException
,则同样适用。循环将一直循环,直到所有冲突都得到解决

此方法是UnitOfWork类的一部分。为了更清楚,我将发布我的代码:

// 'Client-wins' rules, any conflicts found will always cause client changes to
// overwrite anything else.
public void CommitAndRefresh() {
  bool saveFailed;

  do {
    try {
      _session.Transaction.Commit();
      _session.BeginTransaction();
      saveFailed = false;
    } catch (StaleObjectStateException ex) {
      saveFailed = true;

      // Get the staled object with client changes
      var staleObject = _session.Get(ex.EntityName, ex.Identifier);

      // Extract the row-version property name
      IClassMetadata meta = _sessionFactory.GetClassMetadata(ex.EntityName);
      string rowVersionPropertyName = meta.PropertyNames[meta.VersionProperty] as string;

      // Store all property values from client changes
      var propertyValues = new Dictionary<string, object>();
      var publicProperties = staleObject.GetType().GetProperties();
      foreach (var p in publicProperties) {
        if (p.Name != rowVersionPropertyName) {
          propertyValues.Add(p.Name, p.GetValue(staleObject, null));
        }
      }

      // Get latest data for staled object from the database
      _session.Refresh(staleObject);

      // Update the data with the original client changes except for row-version
      foreach (var p in publicProperties) {
        if (p.Name != rowVersionPropertyName) {
          p.SetValue(staleObject, propertyValues[p.Name], null);
        }
      }
      // Merge
      _session.Merge(staleObject);
    }
  } while (saveFailed);
}
//“客户端赢”规则,发现的任何冲突都会导致客户端更改
//覆盖任何其他内容。
公共无效提交刷新(){
布尔保存失败;
做{
试一试{
_Commit();
_session.BeginTransaction();
saveFailed=false;
}捕获(StaleObjectStateException-ex){
saveFailed=true;
//使用客户端更改获取过时的对象
var staleObject=_session.Get(ex.EntityName,ex.Identifier);
//提取行版本属性名称
IClassMetadata meta=\u sessionFactory.GetClassMetadata(ex.EntityName);
字符串rowVersionPropertyName=meta.PropertyNames[meta.VersionProperty]作为字符串;
//存储客户端更改中的所有属性值
var propertyValues=新字典();
var publicProperties=staleObject.GetType().GetProperties();
foreach(publicProperties中的var p){
if(p.Name!=rowVersionPropertyName){
Add(p.Name,p.GetValue(staleObject,null));
}
}
//从数据库中获取过时对象的最新数据
_会话刷新(staleObject);
//使用除行版本之外的原始客户端更改更新数据
foreach(publicProperties中的var p){
if(p.Name!=rowVersionPropertyName){
p、 SetValue(staleObject,PropertyValue[p.Name],null);
}
}
//合并
_session.Merge(staleObject);
}
}while(saveFailed);
}
上面的代码运行良好,可以处理与客户机wins规则的并发性。然而,我想知道NHibernate中是否有任何内置功能可以为我实现这一点,或者是否有更好的方法来处理这一点


提前感谢,

您描述的是缺乏并发检查。如果不使用并发策略(乐观锁、版本或悲观),则不会抛出StaleStateObjectException,并将发布更新


好了,现在我明白你的用例了。重要的一点是,ISession应该在抛出异常后丢弃。您可以使用ISession.Merge来合并分离对象和持久对象之间的更改,而不是自己进行。不幸的是,合并不会级联到子对象,因此您仍然需要自己遍历对象图。因此,实现将类似于:

catch (StaleObjectStateException ex)
{
    if (isPowerUser)
    {
        var newSession = GetSession();
        // Merge will automatically get first
        newSession.Merge(staleObject);
        newSession.Flush();
    }
}

是的,我理解覆盖更改听起来很愚蠢,最好根本不检查冲突,让最后一次更改通过。但我们有一个要求,“超级用户”应该能够覆盖任何内容,而不会被冲突问题所困扰。其他用户应该得到冲突通知,并被问到该怎么做。嗨,杰米,谢谢你的更新。不幸的是,在这种情况下,'newSession.Merge()'将引发另一个'StaleObjectStateException',因为'staleObject'仍然具有旧版本。此外,在我解决了旧代码中的所有冲突后,我会关闭并处理当前会话,并打开一个新会话。为了清晰起见,我没有发布该部分。你确定吗?无论如何,您可以从旧会话中逐出staleObject,使其分离。是的。我尝试在旧会话上对陈旧对象执行execute(),关闭并释放它,然后在新会话上执行Merge(),仍然在Merge()上抛出StaleObjectStateException。当Merge()被调用时,NHibernate对该对象执行SELECT sql操作,当它看到检索到的对象与我试图合并的对象版本不同时,它会抛出异常。