NHibernate стреляет себе в ногу: владение ассоциацией и управление версиями

Ситуация

Предполагать:

  • NHibernate 4
  • Отношения родитель / потомок (один ко многим), однонаправленное отображение от родителя к потомку
  • Inverse(false), т.е. родитель отвечает за ассоциацию
  • Cascade.All от родителя к ребенку
  • Контроль версий (столбец базы данных, который автоматически увеличивается в БД при обновлении записи) включен как для родительского, так и для дочернего элементов (оптимистическая блокировка)

Наблюдаемое поведение:

  1. Открытая сессия и транзакция.
  2. Загрузите родительский объект вместе с его дочерней коллекцией через некоторый запрос.
  3. Добавьте вновь созданный дочерний элемент в дочернюю коллекцию.
  4. SaveOrUpdate родитель.
  5. NHibernate сначала добавляет дочерний элемент в базу данных, давая ему версию.
  6. Затем он заботится о родительской ассоциации (потому что он отвечает за нее) и обновляет внешний ключ дочернего элемента (слава богу, он допускает значение NULL ...), увеличивая версию дочернего элемента. Однако он не обновляет дочерний элемент в сеансе.
  7. Наконец, он применяет каскадирование, а также вызывает AddOrUpdate для дочернего элемента. Единственная проблема: в сеансе есть устаревшая версия, из-за которой выдается ConcurrencyException. NHibernate стреляет себе в ногу.

Вопрос

Почему NHibernate не обновляет дочерние объекты в сеансе, когда применяет логику для обновления ассоциации? Что за этим стоит? Мне кажется, что единственный разумный способ смоделировать родитель / потомок с контролем версий - это использовать Inverse(true).

Уточнение: меня не интересуют какие-либо обходные пути и / или более подходящая конфигурация. Я знаю о них. Меня интересуют только причины поведения NHibernate в описанном сценарии.

Изменить: основная проблема, похоже (imo), что NHibernate не полностью контролирует управление версиями самостоятельно. Скорее, пусть об этом позаботится база данных (по крайней мере, в моей конфигурации). К сожалению, это не очень хорошо работает вместе, потому что NHibernate не ожидает изменения версии в клиенте, когда он обновляет ассоциацию с ним, потому что с более высокого уровня ассоциация принадлежит родителю и, следовательно, никаких изменений в дочернем элементе не происходит. ожидал. Это, по крайней мере, возможное объяснение этого.


person domin    schedule 12.06.2017    source источник
comment
В пункте 6 выше вы говорите: «... увеличивает свою версию». Версия какой сущности изменяется на этом этапе? Родитель?   -  person David Osborne    schedule 13.06.2017
comment
@DavidOsborne: версия дочернего объекта. Это потому, что внешний ключ ребенка был изменен.   -  person domin    schedule 13.06.2017
comment
Если вы хотите, чтобы кто-то изучил, что происходит в вашем случае, и есть ли за этим разумное обоснование, или ошибка на вашей стороне или на стороне NHibernate, или просто неудачная неподдерживаемая комбинация функций и фактически используемых технологий, a минимальный воспроизводимый пример является обязательным: определение класса, сопоставления, используемая база данных, используемые версии программного обеспечения, проверенный код с ними и фактическое воспроизведение ваши проблемы. В противном случае вы в основном надеетесь, что кто-то, уже столкнувшийся с таким же случаем, придет сюда и признает, что, вероятно, это его тот же случай, ...   -  person Frédéric    schedule 14.06.2017
comment
Поиск по принципу "один-ко-многим" не обратной версии дочернего оптимистического сбоя в хорошо известной поисковой системе дает вопрос, который выглядит как близкий к дублированию.   -  person Frédéric    schedule 14.06.2017
comment
@ Фредерик: На самом деле, я намеренно не хотел раздувать вопрос деталями рабочего примера, потому что полный и минимальный в этом случае уже не совсем короткий. ;) Я надеялся, что кто-то узнает о проблеме или причине такого поведения (в зависимости от того, следует ли считать это проблемой). Я думаю, что ответ Дэвида Осборна в этом отношении довольно хорош. Также спасибо за ссылку. Он слишком раздут, чтобы читать подробно, но, похоже, это именно та проблема. Я не ожидал, что что-то настолько фундаментальное может все еще работать с ошибками в NHib.   -  person domin    schedule 14.06.2017
comment
Может быть, потому, что это важно для вас и, вероятно, множества других людей, но не для большинства пользователей. Возможно, им просто не нужно ограничивать свою модель так же, как вашу. Если это на самом деле ошибка (или недостаток дизайна, недостаток, что угодно), но никто из заинтересованных людей не тратит время на заполнение хорошего отчета или даже не пытается исправить его самостоятельно (в конце концов, это открытый исходный код, прочтите здесь), ничего удивительного, что он остается здесь.   -  person Frédéric    schedule 14.06.2017


Ответы (2)


NHibernate стреляет себе в ногу.

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

Почему NHibernate не обновляет дочерние объекты в сеансе, когда применяет логику для обновления ассоциации?

Я думаю, это потому, что изменение ассоциации влияет только на родителя. Если нет обратной ссылки, значит, с точки зрения сущности, в дочернем элементе ничего не изменилось.

Однако он не обновляет дочерний элемент в сеансе.

Какие изменения вы ожидали? Теперь дочерний элемент связан с родителем, но эта связь смоделирована как часть ответственности родителя. Я полагаю, это можно рассматривать по-другому, если дочерний элемент содержит ссылку на своего родителя, но вы не сказали, так ли это в вашем вопросе.

person David Osborne    schedule 13.06.2017
comment
Дочерний элемент не содержит ссылки на родителя. Но я думаю, это не имело бы значения. Обновляя ассоциацию с дочерним элементом, NHibernate должен изменить внешний ключ дочернего элемента, в результате чего версия параллелизма дочернего элемента (хранящаяся в каком-либо столбце записи базы данных дочернего элемента) будет увеличена, что делает сущность в сеансе устаревшей. Из конфигурации он должен знать об этом, а также обновить дочерний элемент в сеансе (в основном просто обновите там поле версии). - person domin; 13.06.2017
comment
[...], потому что изменение связи влияет только на родителя. Если нет взаимной ссылки, то с точки зрения сущности ничего о потомке не изменилось. Мне нравится это утверждение, потому что оно имеет какой-то смысл. Однако я не понимаю, как существование обратной ссылки от дочернего к родительскому могло бы изменить ситуацию. Тем не менее, ничего не изменилось в дочернем элементе с точки зрения сущности, потому что владельцем ассоциации по-прежнему является родитель, верно? - person domin; 14.06.2017
comment
Я пришел к выводу, что если дочерний класс содержит ссылку на своего родителя, тогда происходит изменение «уровня сущности» для дочернего элемента, которое можно наблюдать выше уровня персистентности. Для меня связывание родительского элемента с его дочерними элементами через механизм базы данных - это то, о чем я бы не хотел знать, если я работаю на уровне сущности. Что вы, возможно, обнаружили, так это то, что NH не работает таким образом и рассматривает любые изменения уровня сохраняемости как «версионные». Вы не задумывались о том, чтобы заглянуть в источник? Это может быть очень полезно. - person David Osborne; 14.06.2017

Часть 6, выделенная жирным шрифтом, может быть ошибкой, если вы имеете в виду, что дочерняя версия не извлекается и не обновляется для них. Проверьте https://nhibernate.jira.com и, если ничего, в конце концов сообщите об этом с полным тестовым примером. Хотя бы что-то вроде MCVE. Я не пытаюсь воспроизвести ваш случай, потому что ваш вопрос позволяет слишком много кодировать тем, кто хотел бы проверить, что происходит с вашим сценарием. Я даже не знаю, какой механизм управления версиями вы используете: их много, с разным поведением.
(После вашего редактирования, похоже, что это какая-то сторона базы данных, обрабатываемая счетчиком. Но если вы хотите, чтобы кто-то изучил, что такое происходит в вашем случае, и есть ли за этим рациональное объяснение, или ошибка на вашей стороне, или на стороне NHibernate, или просто неудачная неподдерживаемая комбинация функций и фактически используемых технологий, MCVE по-прежнему необходим: определение класса, сопоставления , используемая база данных, используемые версии программного обеспечения, проверенный код с ними и фактическое воспроизведение ваших проблем.)

Теперь есть вещи, которые вы можете сделать: зачем позволять родителям «отвечать» за детей? Это не имеет ничего общего с каскадом, и вам может потребоваться только каскадирование. Вероятно, это так, если ваше отношение двунаправленное, когда дочерние элементы отображаются обратно на своих родителей. Подробнее здесь.

В противном случае, если ваше отношение является однонаправленным, и если наличие отсоединенных дочерних элементов не нуждается в поддержке, вы должны установить ключ как не допускающий значения NULL и не требующий обновлений от родительского элемента. Подробнее здесь. Это выполнит работу с set, но создаст проблемы с list, такие как отсутствие обновлений индекса и появление дыр в списке.

person Frédéric    schedule 13.06.2017
comment
Возможно, это ошибка, но я настолько новичок в NHibernate, что еще не уверен в этом. Может быть, есть какое-то недоразумение с моей стороны, отсюда и этот вопрос. Но когда я убеждаюсь, что это действительно ошибка, я отправляю заявку. - person domin; 14.06.2017