Как добавить новый объект в IList, отображаемый как один-ко-многим с помощью NHibernate? - PullRequest
4 голосов
/ 10 августа 2009

Моя модель содержит класс Section, который имеет упорядоченный список Statics, которые являются частью этого раздела. Оставляя все остальные свойства, реализация модели выглядит так:

public class Section
{
    public virtual int Id { get; private set; }
    public virtual IList<Static> Statics { get; private set; }
}

public class Static
{
    public virtual int Id { get; private set; }
}

В базе данных отношение реализовано как один-ко-многим, где таблица Static имеет внешний ключ, указывающий на Section, и целочисленный столбец Position для хранения своей позиции индекса в списке. является частью.

Отображение выполняется в Fluent NHibernate следующим образом:

public SectionMap()
{
    Id(x => x.Id);
    HasMany(x => x.Statics).Cascade.All().LazyLoad()
            .AsList(x => x.WithColumn("Position"));
}

public StaticMap()
{
    Id(x => x.Id);
    References(x => x.Section);
}

Теперь я могу загрузить существующие Static s, и я также могу обновить их детали. Тем не менее, я не могу найти способ добавить новые Static s к Section, и эти изменения сохраняются в базе данных. Я пробовал несколько комбинаций:

  • mySection.Statics.Add(myStatic)
  • session.Update(mySection)
  • session.Save(myStatic)

но самое близкое, что я получил (используя первые два оператора), к исключению SQL: «Невозможно вставить значение NULL в столбец« Позиция »». Ясно, что здесь INSERT предпринята попытка, но NHibernate, похоже, не добавляет автоматически позицию индекса в оператор SQL.

Что я делаю не так? Я что-то упускаю в своих отображениях? Нужно ли выставлять столбец Position как свойство и присваивать ему значение самостоятельно?

РЕДАКТИРОВАТЬ: Очевидно, все работает, как ожидалось, если я уберу ограничение NOT NULL для столбца Static.Position в базе данных. Я думаю, NHibernate делает вставку и сразу после обновления строки со значением Position.

Хотя это ответ на вопрос, я не уверен, что он самый лучший. Я бы предпочел, чтобы столбец Position не обнулялся, поэтому я все еще надеюсь, что есть какой-то способ заставить NHibernate предоставить значение для этого столбца непосредственно в операторе INSERT.

Таким образом, вопрос все еще открыт. Любые другие решения?

Ответы [ 3 ]

7 голосов
/ 10 августа 2009

При использовании двунаправленного отношения «один ко многим» в NHibernate один из концов должен быть «обратным». Рекомендуется устанавливать конец коллекции как обратный, поскольку это позволяет избежать ненужных операторов SQL и позволяет столбцу id быть «не нулевым».

В разделе 6.4 документации вы можете найти следующее примечание:

Очень важное примечание: если столбец ассоциации объявлен как NOT NULL, NHibernate может вызвать нарушение ограничений при создании или обновлении ассоциации. Чтобы предотвратить эту проблему, вы должны использовать двунаправленную ассоциацию с многозначным концом (набором или сумкой), помеченным как обратный = "true". Смотрите обсуждение двунаправленных ассоциаций позже в этой главе.

Итак, вам нужно добавить .Inverse () к вашему отображению HasMany в SectionMap.

public SectionMap()
{
    Id(x => x.Id);
    HasMany(x => x.Statics)
        .Cascade.All()
        .LazyLoad()
        .Inverse()
        .AsList(x => x.WithColumn("Position"));
}

Возможно, вам также понадобится метод Add and Remove для Section, который устанавливает / сбрасывает ссылку на статический объект, а также добавляет / удаляет статический элемент в / из его собственной коллекции:

public virtual void AddStatic(Static static)
{
    Statics.Add(static);
    static.Section = this;
}


public virtual void RemoveStatic(Static static)
{
    Statics.Remove(static);
    static.Section = null;
}

Эти методы обеспечивают точность ссылок с обеих сторон отношений.

Согласно разделу 6.8 документов NHibernate не поддерживает двунаправленные связи при использовании проиндексированных коллекций:

Обратите внимание, что NHibernate не поддерживает двунаправленные связи «один ко многим» с индексированной коллекцией (списком, картой или массивом) в качестве «многих» концов, вы должны использовать набор или набор сумок.

Итак, если у вас все еще возникают проблемы, рассмотрите возможность использования однонаправленных отношений вместо двунаправленных, однако это может означать, что ваш столбец внешнего ключа должен иметь значение NULL (согласно примечанию в начале сообщения). В противном случае вам, возможно, придется сопоставить свою коллекцию как сумку или набор вместо списка.

2 голосов
/ 10 августа 2009

В таблице Static у вас есть поле с именем "SectionID" или что-то подобное. Пусть это поле имеет значение NULLable: NHibernate сначала добавляет новую запись, а затем обновляет ссылочный идентификатор.

Я нашел это в своей БД, и я тоже удивлен: почему NH не любит правильные ссылки на таблицы на уровне базы данных?

1 голос
/ 24 января 2013

Я думал, что добавлю свои 0,02 доллара только потому, что пришел сюда в поисках решения вашей проблемы, и похоже, что ни один из ответов на самом деле не работает.

Вы почти в своем решении. Ваше отображение для HasMany правильное. Проблема в том, что, как вы сказали, позиция не обновляется в базе данных (FWIW, когда вы устанавливаете ее в null, она только «работает», потому что NULL вставляется в базу данных; для меня это не совсем работает;) ).

Я нахожу это немного странным, вам нужно также отобразить позицию в вашем классе Static и, очевидно, включить свойство Position в Static. Вы не хотите, чтобы свойство Position было доступно для записи, потому что его значение определяется положением Static в списке.

Добавить следующее в Static

public virtual int Position 
{
    get 
    {
        // Will throw exception if Section is null
        // or Section.Statics is null...
        return Section.Statics.IndexOf(this);
    }
    protected set 
    {
    }
}

При этом в столбце Положение в БД будут обновлены (не забудьте отобразить вашу Позицию в вашей StaticMap).

Я предполагаю, что NHib-прокси Static может обновлять поле Position в зависимости от его позиции в списке (каким-то волшебством, которым я обладаю), и это затем сохраняется в БД.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...