Как изящно объединить графы объектов после NHibernate StaleObjectStateException? - PullRequest
9 голосов
/ 09 ноября 2011

Мы пытаемся объединить объекты после того, как было создано исключение StaleObjectStateException, чтобы сохранить объединенную копию.

Вот наша ситуация с окружающей средой:

  • Элемент списка
  • Мульти-пользовательская система
  • Настольное приложение WPF, база данных SQL Server 2008
  • NHibernate 3.1.0.4000, FluentNHibernate 1.2.0.712
  • Глобальные, длительные сеансы NHibernate [на данный момент.Мы понимаем, что сеанс для каждого докладчика является рекомендуемым шаблоном, но в настоящее время в нашем расписании проекта нет времени для преобразования.]
  • Сохранения сверху вниз и навигация по свойствам (то есть мы сохраняемобъект уровня (в данном документе называемый родительским) в нашем доменном графе)
  • .Cascade.AllDeleteOrphan () используется в большинстве случаев.
  • Пользователи владеют только некоторыми объектами в графе домена, но разделяют права собственности наParent.
  • Свойства навигации для дочерних объектов не существуют.
  • Все классы имеют числовой идентификатор и числовые поля версии.

Вариант использования:

  • Пользователь 1 запускает приложение и открывает Parent.
  • Пользователь 2 запускает приложение и открывает Parent.
  • Пользователь 2 добавляет дочерний элемент (в данном документе C2).
  • Пользователь 2save Parent.
  • Пользователь 1 добавляет дочерний элемент (в данном случае C1).
  • Пользователь 1 сохраняет Parent.
  • Пользователь 1 получает исключение StaleObjectStateException (и это правильно)

Мы хотим Изящно обработать исключение.Поскольку пользователи разделяют права собственности на родителя, пользователь 1 должен иметь возможность успешно сохранить и сохранить родителя как со своим новым дочерним элементом, так и с дочерним элементом пользователя 2.

Когда выбрасывается SOSE, согласно Айенде (http://msdn.microsoft.com/en-us/magazine/ee819139.aspx):

ваш сеанс и его загруженные объекты являются тостами, потому что с NHibernate исключение, выброшенное из сеанса, переводит этот сеанс в неопределенное состояние. Вы больше не можете использовать этот сеанс или любые загруженные объекты

C1 уже был назначен идентификатор и версия # в сеансе, который теперь бесполезен. (Хотелось бы, чтобы это было не так.)

Как мы комбинируем использование ISession.Merge () и ISession.Refresh () для получения недавно сохраненного Parent с C1 и C2?

Мы пробовали несколько загадочных перестановок, ни одна из которых не работает полностью. Обычно, любая строка былаобновлен или удален другой транзакцией (или сопоставление несохраненного значения было неверным), или фактическим конфликтом идентификаторов на уровне ODBC.

Наша теория на данный момент:

  1. Сброс номеров версий на С1 (чтобы предотвратить «неверное сопоставление несохраненных значений»)
  2. Получить новый сеанс
  3. newSession.Refresh (C1);
  4. newParent= newSession.QueryOver [...]
  5. newParent.Add (C1);
  6. newSession.SaveOrUpdate (newParent)

Однако во всей документации предполагается, чтоnewSession.Merge предполагается достаточным.

Другие сообщения, используемые в качестве исследования: Свободный NHibernate Newbie: строка была обновлена ​​или удалена другой транзакцией Есть ли альтернатива ISession.Merge (), которая не генерируется при использовании оптимистической блокировки? Строка StaleObjectstateException была обновлена ​​или удалена Как я могу сказать NHibernate сохранять только измененные свойства Hibernate (JPA): как обрабатывать StaleObjectStateException, когда несколько объектов были изменены и зафиксированы (Java, но я думаю, что это актуально)

1 Ответ

2 голосов
/ 09 ноября 2011

Поскольку пользователи совместно используют владельца родительского элемента, пользователь 1 должен иметь возможность успешно сохранить и сохранить родительский элемент как со своим новым дочерним элементом, так и с дочерним элементом пользователя 2.

ПочемуВы просто отключаете оптимистическую блокировку дочерней коллекции?Тогда любой может добавить дочерние элементы, и это не увеличит версию родительского элемента.

В противном случае, вот решение, которое мой текущий проект использует для всех восстанавливаемых исключений, которые может выдать сеанс (например, соединение с БД потеряно, внешний ключнарушено, ...):

  1. Перед вызовом session.Flush() сеанс сериализуется в MemoryStream.
  2. Если session.Flush() или transaction.Commit() выдает исключение, котороевосстанавливаемый, исходный сеанс удаляется, а сохраненный десериализуется.
  3. Экран вызова получает информацию о том, что сеанс был восстановлен после исключения, и снова вызывает те же запросы, которые были вызваны при открытии экрана,первый раз.И поскольку все измененные объекты все еще находятся в восстановленном сеансе, пользователь теперь находится в состоянии непосредственно перед тем, как он нажал кнопку сохранения.
...