Каскад "многие-ко-многим" в Fluent-NHibernate не заполняет таблицу ссылок

ОК, независимо от того, как я определяю эти сопоставления, мое сопоставление "многие-ко-многим" не хочет работать с каскадной вставкой. Я пробовал различную комбинацию Cascade() с Reverse() и удалял все ненужные свойства, чтобы понять, не связано ли это с тем, что это не работает, но нет блокировки.

Это действительно простой материал: у меня есть Message (например, электронная почта), который отправляется от пользователя (я вызывал объект BasicUser) нескольким пользователям (через свойство To). User и Message в терминах получателей имеют отношение "многие ко многим", но FromUser имеет один-ко-многим. FromUser отлично работает, и он обновляется хорошо, но моя проблема связана со многими-ко-многим. Я даже удалил FromUser и отношения, чтобы проверить, была ли эта проблема, но не помогло.

Итак, вот дизайн таблицы (Удалил связь от FromUser до BasicUser для простоты)

enter image description here

А вот отображения:

public class MessageMap : ClassMap<Message>
{

    public MessageMap()
    {
        Id(x => x.Id).Column("MessageId");
        Map(x => x.Subject);
        Map(x => x.SentAt);
        Map(x => x.Body);
        References(x => x.From).Column("FromUser");
        HasManyToMany(x => x.To).Table("BasicUserMessage").ChildKeyColumn("BasicUserId")
            .ParentKeyColumn("MessageId").Cascade().All();
    }
}

public class BasicUserMap : ClassMap<BasicUser>
{
    public BasicUserMap()
    {
        Id(x => x.Id).Column("BasicUserId");
        Map(x => x.DisplayName);
        Map(x => x.Username);
        HasManyToMany(x => x.Messages).Table("BasicUserMessage").ChildKeyColumn("MessageId")
            .ParentKeyColumn("BasicUserId").Inverse();
    }
}

И я вызываю это, и он не работает (таблица BasicUserMessage не заполняется): (Примечание. Пользователи с идентификаторами 1, 2 и 3 действительно существуют - я также попытался получить их из базы данных, а затем добавить в список все еще не работал)

ISessionFactory factory = GetSessionFactory();
ISession session = factory.OpenSession();
Message m = new Message()
                {
                    Body = "Please note 2",
                    Subject = "Secret 2",
                    From = new BasicUser(){Id = 2},
                    SentAt = DateTime.Now,
                };
m.To.Add(new BasicUser(){Id = 1});
m.To.Add(new BasicUser(){Id=3});
session.SaveOrUpdate(m);
session.Close();

Ответ 1

Ответ на транзакции - это случайный случай. Также случайным случаем является то, что это проявляется как таковое, потому что вы используете что-то вроде генератора IDENTITY, для которого требуется сбой базы данных, чтобы получить идентификатор.

Вот что делает NHibernate, когда вы устанавливаете каскад save-update (или любой каскад, который подразумевает это), из ассоциации "многие-ко-многим" с таким:

Сохраните родительский объект. Это происходит сразу в базе данных из-за стратегии идентификации. Коллекция "изменена" (потому что она новая), поэтому давайте посмотрим на нее. Этот шаг возникает только в том случае, если inverse не задано родительским отображением отношения. Создайте запись в таблице ссылок для каждого из них.

Но подождите, некоторые из этих записей являются временными. Каскады устанавливаются правильно, так что все в порядке - но для того, чтобы создать запись таблицы ссылок в сеансе, нам нужен идентификатор этих детей, поэтому дайте им возможность немедленно сохранить их.

Теперь все соответствующие объекты являются постоянными, а в сеансе есть ожидающая вставка для всех записей в таблице ссылок. Промывка сеанса выдаст команды и создаст эти записи.

Когда вы завершаете свой код в транзакции, транзакция сбрасывает сеанс, поэтому создается при фиксации. Если вы используете генератор идентификаторов, который не требует обмена в оба конца, тогда записи таблицы ссылок и сущности будут вставлены одновременно, поэтому вы не увидите "отключение", которое вы видите, - если сеанс никогда не краснеет, ничто никогда не вставлено, и когда он покраснел, все вставлено. Если у вас нет ни одной из этих вещей, явная очистка вашего сеанса создаст записи таблицы ссылок, и все будет хорошо.

Ответ 2

Вы сделали обе ссылки обратными. Это означает, что NH: не храните его с этой стороны, потому что он уже сохранен другой стороной. Если оба являются обратными, ничего не сохраняется.

Удалить обратную сторону из одной из ссылок.

Ответ 3

Вам нужно обернуть свой код в транзакции. В противном случае Nhibernate не сохранит значения при объединении таблицы

Ответ 4

Вам нужно сделать объекты BasicUser постоянными:

ISessionFactory factory = GetSessionFactory();
ISession session = factory.OpenSession();
Message m = new Message()
                {
                    Body = "Please note 2",
                    Subject = "Secret 2",
                    From = new BasicUser(){Id = 2},
                    SentAt = DateTime.Now,
                };
var basicUser1 = new BasicUser(){Id = 1};
session.Save(basicUser1);
m.To.Add(basicUser1);
var basicUser3 = new BasicUser(){Id = 3};
session.Save(basicUser3);
m.To.Add(basicUser3);
session.Save(m);
session.Flush();

Это, конечно, должно быть сделано в транзакции (как ответил Sly), и сеанс должен быть завернут в оператор using.