Свободно-NHibernate каскад многие-ко-многим не заполняет таблицу ссылок - PullRequest
11 голосов
/ 17 марта 2011

ОК, независимо от того, как я определяю эти сопоставления, мое сопоставление многие ко многим не хочет работать с каскадной вставкой.Я пробовал различные комбинации 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 не заполняется):(Обратите внимание, что пользователи с Id 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();

Ответы [ 4 ]

6 голосов
/ 09 ноября 2011

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

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

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

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

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

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

5 голосов
/ 17 марта 2011

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

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

4 голосов
/ 17 марта 2011

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

0 голосов
/ 17 марта 2011

Вам необходимо сделать объекты 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();

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

...