Alexey Suvorov dev blog

Мой разработческий блог

Archive for the ‘nHibernate’ Category

NHibernate.StaleStateException и порядок следования в мапингах nHibernate

leave a comment »

Все примеры orm показывают как использовать их правильно, но в реальной жизни их зачастую используют неправильно по соображениям удобства или производительности. В основном проекте мы используем nHibernate и делаем это неправильно :).

Если в 2-х словах то есть контейнер, который содержит назовем их виджеты, еще у контейнера есть сущности которые позволяют управлять некоторыми параметрами целой группы виджетов.

nHibernate

Так как ни группа ни свойства заранее неизвестны, то в базе они хранятся как ассоциация состоящая из WidgetId и PropertyId (на схеме PropertyId пропущено т.к. несущественно). Вся проблема заключается в том, что при удалении контейнера нужно удалять все виджеты и все мапинги на них. Но т.к по соображениям удобства мапинг содержит только WidgetId а не сам виджет, то nHibernate ничего не знает про эту связь и логичным решением поддержания базы в консистентном состоянии видится внешний ключ из таблицы мапингов на таблицу виджетов. Но тут же возникает проблема при удалении самого контейнера. nHibernate как ответственный бухгалтер считает что он сам должен удалить все учтённые мапинги и выстреливает исключением NHibernate.StaleStateException: Unexpected row count: 0; expected: 1 если вдруг база данных самовольно сделала это за него.

Решение оказалось очень простым — порядок следования деклараций в hbm файле является значимым и помещение тега коллекции мапингов до тега коллекции виджетов спасает ситуацию.

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping assembly="..." 
                   namespace="..." 
                   xmlns="urn:nhibernate-mapping-2.2">
  <class name="Container">
    <id name="Id">
      <generator class="..." />
    </id>
    
   <set name="CommonParameters" table="CommonPropertyMapping" cascade="all-delete-orphan">
      <key column="ContainerId" not-null="true" />
      <one-to-many class="CommonProperty" />
   </set>
   <set name="Widgets" table="Widget" cascade="all-delete-orphan">
      <key column="ContainerId" not-null="false" />
      <one-to-many class="Widget"/>
   </set>
  </class>
</hibernate-mapping>

Немного криво? Полностью согласен! Но мы живем в реальном мире.

Written by alexeysuvorov

06.06.2013 at 7:21 дп

Опубликовано в .net, nHibernate

Tagged with