Як видалити дочірній об'єкт у NHibernate?


79

У мене є батьківський об'єкт, який має відношення "один до багатьох" з IList дочірніх об'єктів. Який найкращий спосіб видалити дочірні об’єкти? Я не видаляю батьків. Мій батьківський об'єкт містить IL-список дочірніх об'єктів. Ось відображення взаємовідносин один до багатьох:

<bag name="Tiers" cascade="all">
  <key column="mismatch_id_no" />
  <one-to-many class="TGR_BL.PromoTier,TGR_BL"/>
</bag>

Якщо я намагаюся видалити всі об'єкти з колекції за допомогою clear (), то викликаю SaveOrUpdate (), я отримую такий виняток:

System.Data.SqlClient.SqlException: Cannot insert the value NULL into column

Якщо я намагаюся видалити дочірні об'єкти окремо, а потім видалити їх з батьківського, я отримую виняток:

deleted object would be re-saved by cascade

Я вперше маю справу з видаленням дочірніх об’єктів у NHibernate. Що я роблю не так?

редагувати: Просто для уточнення - Я НЕ намагаюся видалити батьківський об’єкт, а лише дочірні об’єкти. У мене стосунки встановлені як стосунки з багатьма з батьків. Чи потрібно мені також створювати взаємозв'язок "багато-до-одного" на відображенні дочірнього об'єкта?

Відповіді:


139

Ви отримуєте першу помилку, тому що, коли ви вилучаєте елементи з колекції, за замовчуванням режим роботи NHibernate полягає у простому розриві асоціації. У базі даних NHibernate намагається встановити для стовпця зовнішнього ключа в дочірньому рядку значення null. Оскільки ви не допускаєте значення null у цьому стовпці, SQL Server викликає помилку. Очищення колекції не обов'язково видалить дочірній об'єкт, але один із способів це зробити - встановити cascade = all-delete-orphan. Це повідомляє NHibernate про те, що він повинен видалити щойно осиротілі рядки, а не встановлювати стовпець зовнішнього ключа.

Ви отримуєте другу помилку, оскільки під час виклику SaveOrUpdate NHibernate спочатку видаляє всі дочірні об’єкти. Потім, оскільки жоден з відносин не позначений як зворотний, NHibernate також намагається встановити для стовпця зовнішнього ключа у вашій дочірній таблиці значення null. Оскільки рядки вже видалено, ви отримуєте другу помилку. Вам потрібно встановити inverse = true на одній стороні ваших стосунків, щоб це виправити. Зазвичай це робиться з одного боку (первинний ключ або батьківський). Якщо ви цього не зробите, NHibernate зробить відповідні оновлення для кожної сторони відносин. На жаль, запуск двох оновлень не є належним чином робити.

Ви завжди повинні позначати одну сторону своїх стосунків як зворотну. Залежно від того, як ви кодуєте, вам може знадобитися або не потрібно використовувати каскад. Якщо ви хочете скористатися одним видаленням знімка, як ви намагаєтеся зробити за допомогою Clear (), вам потрібно визначити ваш каскад.


Тільки для уточнення - на даний момент я лише визначив зв’язок на об’єкті батьків. Я намагався встановити каскад для цього об'єкта на "все" та "все-видалити-сирота" з однаковими результатами обидва рази. Ви маєте на увазі, що мені також потрібно визначити взаємозв'язок "багато-до-одного" на дочірньому об'єкті?
Марк Струзінський

І в цьому випадку чи відноситься inverse = "true" на батьківський або дочірній об'єкт?
Марк Струзінський

3
він належить до класу, який не містить зовнішнього ключа ... зазвичай батьківського.
pmlarocque

Дякую за це пояснення, знову мене спіймала зворотна проблема, і бачити, як це пояснюється, справді допомогло.
Марк Дікінсон,

Це справді допомогло у вирішенні мого питання. Дякую.
Робін Робінсон,

3

Відповідно до відповіді Чака, я вирішив свою проблему, додавши Inverse = true у відображення батьківської сторони:

Повідомлення має багато MessageSentTo:

[HasMany(typeof(MessageSentTo), Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Inverse = true)]
public IList<MessageSentTo> MessageSendTos
{
    get { return m_MessageSendTo; }
    set { m_MessageSendTo = value; }
}

Я використовую Castle ActiveRecord. Дякую, Чак.


2

Спробуйте використовувати merge () замість saveOrUpdate (). Крім того, переконайтеся, що для вашого каскаду встановлено значення all-delete-siroth і що ваші стосунки між батьками та дітьми є інвертованими (inverse = true на батьківському, а потім поле в дочірньому полі, яке є батьківським ідентифікатором з not-null = true) .


2

У нашому прикладі ми маємо категорії з багатьма продуктами, де товар не можна обнулити.

Ви можете вирішити проблему, видаливши продукт і видаливши його з батьківської колекції перед очищенням, але ми все ще шукаємо кращого рішення для цього.

product = pRepo.GetByID(newProduct.ProductID);
product.Category.Products.Remove(product);
pRepo.Delete(product);

Сподіваюся, це все одно допоможе


1
Для цього потрібне інше сховище.
UpTheCreek,

0

Змініть значення атрибуту каскаду з "all" на "all-delete-siroth".


Спробував це. Я все ще отримую той самий виняток.
Марк Струзінський

16
all-delete-orphan видаляє дітей, коли ви видаляєте батьківського. це не має нічого спільного з видаленням дітей, про що запитує цей хлопець.
Kyle West

-3

set Not-Null = true у вашому відображенні в стовпці, що викликає проблему. Хоча я не впевнений у точному синтаксисі (вибачте).


Добре, я встановив для стовпця значення not-null = "true" на відображенні дочірнього об'єкта. Отже, я викликаю метод clear () на IList дочірніх об’єктів, а потім спробую SaveOrUpdate (). Я все ще отримую помилку "Не вдається вставити значення NULL у стовпець". Я роблю щось інше неправильно?
Марк Струзінський

в якому стовпці виникає помилка? як саме ви вилучаєте дітей з вашої моделі?
Kyle West

Я викликаю метод clear () для дочірнього об'єкта Ilist у моєму батьківському об'єкті. Тоді я телефоную SaveOrUpdate () батьківському.
Марк Струзінський
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.