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

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

Вот наша экологическая ситуация:

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

Случай использования:

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

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

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

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

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

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

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

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

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

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

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

1 ответ

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

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

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

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