Как удалить дочерний объект в NHibernate?


79

У меня есть родительский объект, который имеет отношение «один ко многим» с IList дочерних объектов. Как лучше всего удалить дочерние объекты? Я не удаляю родителя. Мой родительский объект содержит список дочерних объектов. Вот отображение отношения "один ко многим":

<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. Что я делаю неправильно?

edit: Просто чтобы уточнить - я НЕ пытаюсь удалить родительский объект, а только дочерние объекты. Я установил отношения один-ко-многим с родителем. Нужно ли мне также создавать отношения «многие к одному» в сопоставлении дочерних объектов?

Ответы:


139

Вы получаете первую ошибку, потому что, когда вы удаляете элементы из коллекции, режим работы NHibernate по умолчанию - просто разорвать ассоциацию. В базе данных NHibernate пытается установить для столбца внешнего ключа дочерней строки значение NULL. Поскольку вы не допускаете пустых значений в этом столбце, SQL Server вызывает ошибку. Очистка коллекции не обязательно приведет к удалению дочернего объекта, но один из способов сделать это - установить cascade = all-delete-orphan. Это сообщает NHibernate, что он должен удалить только что потерянные строки вместо установки столбца внешнего ключа.

Вы получаете вторую ошибку, потому что при вызове SaveOrUpdate NHibernate сначала удаляет все дочерние объекты. Затем, поскольку ни одна связь не помечена как обратная, NHibernate также пытается установить для столбца внешнего ключа в вашей дочерней таблице значение NULL. Поскольку строки уже были удалены, вы получите вторую ошибку. Чтобы исправить это, вам нужно установить inverse = true на одной стороне ваших отношений. Обычно это делается на одной стороне (первичный или родительский). Если вы этого не сделаете, NHibernate сделает соответствующие обновления для каждой стороны отношения. К сожалению, запускать два обновления - не лучший вариант.

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


Просто чтобы прояснить - у меня сейчас только отношения, определенные для родительского объекта. Я пытался установить каскад для этого объекта на «все» и «все-удалить-сирота» с одинаковыми результатами оба раза. Вы имеете в виду, что мне нужно также определить отношение «многие к одному» для дочернего объекта?
Марк Струзински

И в этом случае относится ли inverse = "true" к родительскому или дочернему объекту?
Марк Струзински

3
он принадлежит к классу, который не содержит внешнего ключа ... обычно родительскому.
pmlarocque 01

Спасибо за это объяснение, меня снова поймала обратная проблема, и то, что она объяснилась, действительно помогло.
Марк Дикинсон

Это действительно помогло решить возникшую у меня проблему. Благодарю.
Робин Робинсон,

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-orphan и что ваши родительско-дочерние отношения обратимы (inverse = true для родителя, а затем поле в дочернем элементе, которое является родительским идентификатором с not-null = true) .


2

В нашем примере у нас есть категории со многими продуктами, в которых продукт не допускает значения NULL.

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

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

Надеюсь, это все равно поможет


1
Однако для этого требуется другой репозиторий.
UpTheCreek

0

Измените значение атрибута каскада с «all» на «all-delete-orphan».


Пробовал это. У меня все еще то же исключение.
Марк Струзински

16
all-delete-orphan удаляет потомков при удалении родителя. это не имеет ничего общего с удалением детей, о которых спрашивает этот парень.
Кайл Уэст

-3

установите Not-Null = true в сопоставлении столбца, вызывающего проблему. Я не уверен в точном синтаксисе (извините).


Хорошо, я установил для столбца значение not-null = "true" в сопоставлении дочернего объекта. Итак, я вызываю метод clear () в списке IList дочерних объектов, а затем пробую SaveOrUpdate (). Я все еще получаю сообщение об ошибке «Не удается вставить значение NULL в столбец». Я что-то еще делаю не так?
Марк Струзински

в каком столбце возникает ошибка? как именно вы удаляете детей в своей модели?
Кайл Уэст

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