Свободная проблема каскада NHibernate - Попытка вставить NULL ID - PullRequest
13 голосов
/ 08 марта 2011

У меня есть следующие модели и сопоставления (фрагменты кода приведены ниже).

С одним соревнованием с самого начала должно быть связано несколько ответов Competition (множественный выбор).

В настоящее времяс помощью отображений Fluent NHibernate, показанных ниже, при создании нового объекта Competition, заполнении свойств, создании 3 новых объектов CompetitionAnswer и добавлении их в свойство CompetitionAnswers (свойство Competition), я бы ожидал вызвать Save в сеансекоторый вставил бы 1 строку Соревнования и 3 строки Соревнования Ответа в БД.

Однако, как только я пытаюсь вызвать Save в сеансе, он жалуется, что CompetitionId является нулем, и он не может вставить ноль втаблица CompetitionAnswers для этого поля - что правильно, однако не следует предполагать, что NHibernate сначала создаст Competition, а затем использует вновь сгенерированное значение IDENTITY (CompetitionId) в таблице CompetitionAnswers?

конкуренion (Модель)

public virtual int CompetitionId { get; private set; }
public virtual string Title { get; set; }
public virtual string Description { get; set; }
public virtual IList<CompetitionAnswer> CompetitionAnswers { get; set; }

CompetitionAnswer (Модель)

public virtual int CompetitionAnswerId { get; set; }
public virtual string Answer { get; set; }
public virtual Competition Competition { get; set; }

CompetitionMap (отображение беглого NHibernate)

public CompetitionMap()
{
    Id(x => x.CompetitionId)
        .GeneratedBy.Native();
    Map(x => x.Title);
    Map(x => x.Description);
    HasMany(x => x.CompetitionAnswers)
        .Cascade.AllDeleteOrphan()
        .KeyColumn("CompetitionId")
        .Inverse();
    Table("Competitions");
}

CompetitionAnswerMap (Fluent NHibernate Mapping)

public CompetitionAnswerMap()
{
    Id(x => x.CompetitionAnswerId)
        .GeneratedBy.Native();
    Map(x => x.Answer);
    References(x => x.Competition)
        .Column("CompetitionId");
    Table("CompetitionAnswers");
}

Вот пример кода, который я использовал для тестирования этого сценария, который генерирует ошибку:

Competition c = new Competition();

c.Description = "Description";
c.Title = "Title";

CompetitionAnswer a1 = new CompetitionAnswer { Answer = "Answer 1" };
CompetitionAnswer a2 = new CompetitionAnswer { Answer = "Answer 2" };
CompetitionAnswer a3 = new CompetitionAnswer { Answer = "Answer 3" };

c.CompetitionAnswers.Add(a1);
c.CompetitionAnswers.Add(a2);
c.CompetitionAnswers.Add(a3);

session.Save(c);

Точная ошибка, которую я получаю при попытке сохранить:

Невозможно вставить значение NULL в столбец 'CompetitionId', таблица 'CompetitionAnswers';столбец не допускает пустых значений.Вставить не удается.Заявление было прекращено.

Кто-нибудь может пролить свет на то, почему это в настоящее время не работает?

Ответы [ 3 ]

22 голосов
/ 08 марта 2011

Я почти уверен, не на 100%, что проблема заключается в спецификации Inverse () в вашем отображении CompetitionAnswers on Competition. Inverse () указывает, что дочерние записи отвечают за определение их отношения с родителем. Чаще всего сторона «один» в отношении «один ко многим» (родитель) является «вершиной» графа объекта и «владеет» отношениями со своими дочерними элементами. У родителей есть дети, и решение о том, оставить или отдать ребенка на усыновление, остается за родителем. Однако это не всегда так; в колледже могут быть студенты, но именно студенты имеют реальную возможность решать, куда они пойдут. Здесь «Студент» является «вершиной» графика, а «Школа» - это просто монолитная запись, определяющая посещаемость «Студента». Студент может перевестись в любое время; это их решение, и оно на самом деле не меняет Школу каким-либо значимым образом, поэтому ученики несут ответственность за идентификацию своей принадлежности к школе.

Ваш случай - первый: у Конкурсов есть Ответы на Конкурсы, и у ребенка нет логической обязанности сказать: «Я участвую в Конкурсе»; Конкурс вместо этого «владеет» своей коллекцией ответов. Удаление инструкции Inverse () должно заставить NH рассматривать «Конкурс» как «вершину» графа объектов, поэтому NH вставит «Конкурс», а затем «CompetitionAnswers», который теперь может ссылаться на идентификатор своего родителя.

Еще одна вещь, не связанная с этой проблемой, но если вы сопоставляете базу данных MS SQL Server, а столбец идентификатора определен как столбец идентификации в БД, я бы указал GeneratedBy.Identity() для столбцов идентификатора. Native() ДОЛЖЕН заканчиваться использованием Identity, но он также проверит, доступны ли методы HiLo или Sequence.

5 голосов
/ 08 марта 2011
References(x => x.Competition)
    .Column("CompetitionId")
    .Not.Nullable(); //add this

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

a1.Competition = c; 

Был ли этот столбец non null на стороне базы данных, но отображен как обнуляемый на стороне NH?

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

В ваших отображениях нет ничего плохого.Вероятно, проблема с вашей базой данных.Если вы используете столбцы Identity для своих первичных ключей, для столбца CompetitionId должно быть установлено значение NULL.NHibernate работает, когда у вас есть новый объект с дочерними объектами, который сохраняется, если он вставляет родительский объект и дочерние объекты.Затем он обновляет внешние ключи дочернего объекта новым идентификатором родительского объекта.

Я нашел это NHibernate каскадное сохранение

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