Свободное владение NHibernate со ссылками "многие ко многим"

У меня есть объект под названием «Книги», в котором может быть список других книг под названием «Связанные книги».

Сокращенный объект Book выглядит примерно так:

public class Book
{
      public virtual long Id { get; private set; }

      public virtual IList<Book> RelatedBooks { get; set; }
}

Вот как выглядит сопоставление этих отношений

HasManyToMany(x => x.RelatedBooks)
                .ParentKeyColumn("BookId")
                .ChildKeyColumn("RelatedBookId")
                .Table("RelatedBooks")
                .Cascade.SaveUpdate();

Вот образец данных, которые затем создаются в таблице «Связанные книги»:

BookId     RelatedBookId
1          2
1          3

Проблема возникает, когда я пытаюсь удалить книгу. Если я удалю книгу с идентификатором 1, все будет работать нормально, а из таблицы «Связанные книги» будут удалены две соответствующие записи. Однако, если я попытаюсь удалить книгу с идентификатором 3, я получаю сообщение об ошибке «Оператор DELETE конфликтует с ограничением REFERENCE« FK5B54405174BAB605 ». Конфликт произошел в базе данных« Test », таблица« dbo.RelatedBooks », столбец« RelatedBookId » '".

По сути, происходит то, что книга не может быть удалена, потому что запись в таблице RelatedBooks с идентификатором RelatedBookId, равным 3, никогда не удаляется.

Как мне удалить эту запись при удалении книги?

ИЗМЕНИТЬ

После изменения Cascade с SaveUpdate () на All () та же проблема все еще существует, если я попытаюсь удалить книгу с идентификатором 3. Также с Cascade, установленным на All (), при удалении книги с идентификатором 1, затем все 3 книги (ID: 1, 2 и 3) удаляются, так что это тоже не сработает.

Глядя на SQL, который выполняется, когда вызывается метод Book.Delete (), когда я удаляю книгу с идентификатором 3, похоже, что оператор SELECT смотрит не на тот столбец (что, как я предполагаю, означает, что статус SQL DELETE совершит ту же ошибку, поэтому никогда не удалит эту запись). Вот SQL для связанной книги

SELECT relatedboo0_.BookId as BookId3_
       , relatedboo0_.RelatedBookId as RelatedB2_3_ 
       , book1_.Id as Id14_0_ 

FROM RelatedBooks relatedboo0_ 
     left outer join [Book] book1_ on relatedboo0_.RelatedBookId=book1_.Id 

WHERE relatedboo0_.BookId=3

В данном конкретном случае статус WHERE должен выглядеть примерно так:

WHERE relatedboo0_.RelatedBookId = 3

РЕШЕНИЕ

Вот что мне нужно было сделать, чтобы он работал во всех случаях

Отображение:

HasManyToMany(x => x.RelatedBooks)
                .ParentKeyColumn("BookId")
                .ChildKeyColumn("RelatedBookId")
                .Table("RelatedBooks");

Код:

var book = currentSession.Get<Book>(bookId);

if (book != null)
{
    //Remove all of the Related Books
    book.RelatedBooks.Clear();

    //Get all other books that have this book as a related book
    var booksWithRelated = currentSession.CreateCriteria<Book>()
                                .CreateAlias("RelatedBooks", "br")
                                .Add(Restrictions.Eq("br.Id", book.Id))
                                .List<Book>();

    //Remove this book as a Related Book for all other Books
    foreach (var tempBook in booksWithRelated)
    {
        tempBook.RelatedBooks.Remove(book);
        tempBook.Save();
    }

    //Delete the book
    book.Delete();
}

person Jeremy    schedule 05.02.2010    source источник
comment
Вы на правильном пути, но это решение будет очень неэффективным, потому что оно загружает ВСЕ книги, а затем фильтрует их. Вам следует выяснить, как переписать его, чтобы вернуть только те книги, для которых эта книга должна быть удалена, в их коллекции «Связанные книги». Вы также можете рассмотреть возможность выполнения этой операции через SQL.   -  person Jamie Ide    schedule 08.02.2010
comment
Спасибо за совет. Добавлен запрос критериев NHibernate, чтобы получать только эти книги.   -  person Jeremy    schedule 08.02.2010


Ответы (2)


Вместо того, чтобы устанавливать атрибут cascade, я думаю, вам нужно просто очистить коллекцию RelatedBooks перед удалением книги.

book.RelatedBooks.Clear();
session.Delete(book);

Каскадное удаление обычно не выполняется в отношении «многие ко многим», поскольку при этом удаляется объект на другом конце отношения, в данном случае Книга.

person Jamie Ide    schedule 08.02.2010

Это только что было обновлено:

http://fluentnhibernate.lighthouseapp.com/projects/33236/tickets/115-self-referencing-relationships

person Matthew McLeod    schedule 04.05.2010