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

时间:2011-12-07 01:00:51

标签: nhibernate concurrency merge

我正在尝试找到处理此异常的完美方法,并强制客户端更改覆盖导致冲突的任何其他更改。我想出的方法是将循环中的Session.Transaction.Commit()包装在循环中,在循环内我会做一个try-catch块并通过复制其属性来单独处理每个陈旧对象,除了row-version属性然后刷新对象以获取最新的数据库数据,然后将原始值重新复制到刷新的对象,然后进行合并。一旦我循环,我将提交,如果发生任何其他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);
}

上面的代码工作正常,并使用client-wins规则处理并发。但是,我想知道NHibernate中是否有任何内置功能可以为我执行此操作,或者是否有更好的方法来处理此问题。

提前致谢,

1 个答案:

答案 0 :(得分:1)

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


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

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