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

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

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

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

Пример использования:

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

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

Когда запускается SOSE, согласно Айенде (http://msdn.microsoft.com/en-us/magazine/ee819139.aspx):

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

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

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

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

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

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

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

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

person BufferUnderrunOK    schedule 08.11.2011    source источник


Ответы (1)


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

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

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

  1. Перед вызовом session.Flush() сеанс сериализуется в MemoryStream.
  2. Если session.Flush() или transaction.Commit() вызывает исключение, которое можно восстановить, исходный сеанс удаляется, а сохраненный десериализуется.
  3. Затем экран вызова получает информацию о том, что сеанс был восстановлен после исключения, и снова вызывает те же запросы, которые были вызваны при первом открытии экрана. И поскольку все измененные объекты все еще находятся в восстановленном сеансе, пользователь теперь имеет состояние, в котором он находился непосредственно перед нажатием кнопки «Сохранить».
person cremor    schedule 09.11.2011
comment
Похоже, нам нужно как исправить наши файлы сопоставления, так и затем использовать session.Replicate (obj, ReplicationMode.overwrite) - материал потока памяти не работал, если все не было сериализуемым, и все же наши номера версий снова вызывали StaleObject - person BufferUnderrunOK; 14.11.2011
comment
Примечание: это все еще не мой любимый ответ, но кремор заслужил награду. :) - person BufferUnderrunOK; 21.11.2011