NHibernate - Обработка StaleObjectStateException, чтобы всегда фиксировать изменения клиента - Нужен совет / рекомендация - PullRequest
3 голосов
/ 07 декабря 2011

Я пытаюсь найти идеальный способ обработки этого исключения и заставить изменения клиента перезаписывать любые другие изменения, вызвавшие конфликт. Подход, который я предложил, заключается в том, чтобы заключить вызов 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 Ответ

1 голос
/ 07 декабря 2011

То, что вы описываете, - это отсутствие проверки параллелизма. Если вы не используете стратегию параллелизма (оптимистическая блокировка, версия или пессимистическая), исключение StaleStateObjectException не будет выдано, и будет выпущено обновление.


Хорошо, теперь я понимаю ваш вариант использования. Одним из важных моментов является то, что ISession должен быть отброшен после возникновения исключения. Вы можете использовать ISession.Merge для объединения изменений между отсоединенным постоянным объектом, а не делать это самостоятельно. К сожалению, Merge не распространяется на дочерние объекты, поэтому вам все равно придется самостоятельно обходить граф объектов. Таким образом, реализация будет выглядеть примерно так:

catch (StaleObjectStateException ex)
{
    if (isPowerUser)
    {
        var newSession = GetSession();
        // Merge will automatically get first
        newSession.Merge(staleObject);
        newSession.Flush();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...