Fluent NHibernate Cascade Issue - попытка вставить идентификатор NULL
У меня есть следующие модели и сопоставления (далее фрагменты кода).
Один конкурс должен иметь несколько связанных с ним критериев (множественный выбор) с самого начала.
В настоящее время, используя приведенные ниже сопоставления Fluent NHibernate, когда я создаю новый объект Конкурса, заполняйте свойства, затем создайте 3 новых объекта CompetitionAnswer и добавьте их в свойство CompetitionAnswers (свойство Competition), я бы ожидал для вызова "Сохранить" на сеансе, который будет ВСТАВИТЬ 1 строку конкурса и 3 строки "Соревнование" в БД.
Однако, как только я попытаюсь вызвать Save на сеансе, он жалуется, что CompetitionId имеет значение null и не может вставить нуль в таблицу CompetitionAnswers для этого поля - это правильно, но это не должно, Я предположил, что NHibernate сначала создаст Конкурс, а затем использует вновь созданное значение IDENTITY (CompetitionId) в таблице CompetitionAnswers?
Конкурс (модель)
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 (свободное отображение NHibernate)
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'; колонка не Разрешить null. INSERT терпит неудачу. утверждение завершено.
Кто-нибудь может пролить свет на то, почему это не работает в настоящее время?
Ответы
Ответ 1
Я уверен, а не 100%, что проблема заключается в спецификации Inverse() в вашем сопоставлении CompetitionAnswers on Competition. Inverse() указывает, что дочерние записи отвечают за определение их отношения к родительскому элементу. Чаще всего "одна" сторона "один ко многим" (родительский) является "вершиной" графа объектов и "владеет" отношениями со своими дочерними элементами. У родителей есть дети, и решение о том, следует ли сохранить или отдать ребенка за усыновление, является родительским. Однако это не всегда так; у колледжа могут быть студенты, но это студенты, которые имеют реальную власть решать, куда они поедут. Здесь Студент является "вершиной" графика, а Школа - это монолитная запись, идентифицирующая посещаемость учеников. Студент может передавать в любое время; это их решение, и это действительно не меняет Школу каким-либо значимым образом, поэтому учащиеся несут ответственность за идентификацию себя как принадлежащих к Школе.
Ваше дело является первым: у соревнований есть соревнования, а у ребенка логически не сказано: "Я принадлежу к Конкурсу"; Конкурс вместо этого "владеет" своей коллекцией ответов. Удаление инструкции Inverse() должно сделать NH treat Competition "верхним" графа объекта, поэтому NH будет вставлять Конкурс, а затем в Конкурс, который теперь может ссылаться на их родительский идентификатор.
Еще одна вещь, не связанная с проблемой, но если вы сопоставляетесь с базой данных MS SQL Server, а столбец идентификатора определен как столбец идентификатора в БД, я бы указал GeneratedBy.Identity()
для столбцов идентификатора. Native()
ДОЛЖНО в конечном итоге использовать Identity, но также проверит, доступны ли методы HiLo или Sequence.
Ответ 2
References(x => x.Competition)
.Column("CompetitionId")
.Not.Nullable(); //add this
а также убедитесь, что ребенок указывает на родителя в дополнение к родительскому языку, содержащему дочерний элемент в коллекции. Это необходимо, когда родительский объект обратный.
a1.Competition = c;
Был ли этот столбец non null
на стороне базы данных, но отображался как NULL на стороне NH?
Ответ 3
Нет ничего плохого в ваших сопоставлениях. Вероятно, проблема связана с вашей базой данных. Если вы используете столбцы Identity для ваших первичных ключей, столбец CompetitionId должен иметь значение NULL. Как работает NHibernate, когда у вас есть новый объект с сохраненными дочерними элементами, он вставляет родительский объект и дочерние объекты. Затем он обновляет внешние ключи дочернего объекта новым идентификатором родительского объекта.
Я нашел это каскадное сохранение NHibernate