Каскад "многие-ко-многим" в 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.