Каскад удаления не работает с NHibernate

#c# #nhibernate #fluent-nhibernate #cascade #nhibernate-cascade

#c# #nhibernate #свободно-nhibernate #каскад #nhibernate-каскад

Вопрос:

У меня есть табличное сообщение, в котором есть ссылка на PersonCompany. В сопоставлении для PersonCompany я определил каскадное удаление для этой ссылки:

 this.HasMany(x => x.Communications)
  .AsSet()
  .KeyColumn("PersonCompanyId")
  .Fetch.Select()
  .Inverse()
  .Cascade.Delete();
  

Но когда я сейчас выполняю резервный HQL-запрос:

 var sql = "delete from PersonCompany where Person.Id in (:idList) or Company.Id in (:idList)";
  

с

 var query = NHibernateHelper.CurrentSession.CreateQuery(sql);
query.SetParameterList("idList", contactIdList);
query.SetTimeout(0);
query.ExecuteUpdate();
  

Я всегда получаю это SQLException:

Оператор DELETE конфликтовал со ссылочным ограничением «FK_PersonCompany_Communication». Конфликт произошел в базе данных «proconact», таблица «dbo.Communication», столбец «PersonCompanyId». Оператор был завершен.

Я думаю, что NHibernate теперь должен каскадно удалять записи, на которые ссылаются в сообщении, — не так ли?

Я надеюсь, что кто-нибудь может мне помочь, что я делаю неправильно.

Ответ №1:

Синтаксис, который вы использовали, на самом деле является частью

которые на самом деле используются для МАССОВОЙ работы на сервере БД. Они используют синтаксис HQL, но не охватывают каскад (потому что они выполняются не в памяти, а только на стороне БД)

Таким образом, мы можем загружать объекты, подлежащие удалению … и явно удалять их. Это вызовет каскады:

 //var sql = "delete from PersonCompany where Person.Id in (:idList) or Company.Id in (:idList)";
var sql = "from PersonCompany where Person.Id in (:idList) or Company.Id in (:idList)";
var query = NHibernateHelper.CurrentSession.CreateQuery(sql);
query.SetParameterList("idList", contactIdList);
query.SetTimeout(0);
//query.ExecuteUpdate();
var list = query.List<PersonCompany >();
foreach (var item in list)
{
    session.Delete(item);
}
session.Flush();
  

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

Комментарии:

1. В моем понимании, каскадное удаление будет работать с кодом из «BennoDual», если он установлен рядом с базой ON DELETE CASCADE данных. Это правильное понимание?

2. @A_J, я бы поспорил, что ты знаешь ответ. НО я бы сказал, что применение каскада на стороне БД будет просто выполняться независимо от любого внешнего обработчика инструментов SQL (включая ORM, NHibernate). Я бы не стал рассматривать это как » код будет работать, если установлена база данных …», потому что в этом случае он находится вне домена приложения, вне наших рук. Наш код был просто тихим триггером. Рано или поздно этот параметр (а также магические триггеры) приведет к долгому и разочаровывающему поиску «почему эти строки исчезли». Другими словами, мой опыт защитил бы меня от сравнения скрытых настроек БД и сопоставления / кода приложения.

3. Согласен. Но ваш ответ меня смутил. Если ограничение на стороне базы данных может обрабатывать каскады, почему он должен загружать все объекты только для их удаления? Вы предлагаете это для согласованности кэширования?

4. @A_J Триггер на стороне БД приходит с вашим комментарием. Ни с вопросом, ни с моим ответом. Не уверен, как это может вас смутить… Я (в своем ответе) работаю только с каскадированием NHIBERNATE… и это работает на стороне приложения, только если вы будете загружать данные в сеанс. Надеюсь, теперь это немного более понятно

Ответ №2:

способ сделать это,

 IList<PersonCompany> _pCompanies = ....; <load the required person companies>
foreach (var pc in _pCompanies)
{
_session.delete(pc);
}
  

потому что, когда вы используете массовые обновления, встроенные ограничения не будут работать в Nhibernate. Вы могли бы попытаться создать ограничение уровня БД.