我有以下型号&映射(下面的代码片段)。
一场比赛必须从一开始就有多个与之相关的CompetitionAnswers(多选)。
目前,使用下面显示的Fluent NHibernate映射,当我创建一个全新的Competition对象时,填充属性,然后创建3个全新的CompetitionAnswer对象并将它们添加到CompetitionAnswers属性(竞争中的属性),我希望在会话上调用Save,它会将1个Competition行和3个CompetitionAnswer行插入到DB中。
但是,当我尝试在会话上调用Save时,它会抱怨CompetitionId为null并且无法在该字段的CompetitionAnswers表中插入null - 这是对的,但是,它不应该我假设NHibernate首先创建竞赛,然后在CompetitionAnswers表中使用新生成的IDENTITY值(CompetitionId)?
比赛(模特)
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';专栏没有 允许空值。 INSERT失败。该 声明已被终止。
任何人都可以详细说明为什么目前没有这个工作吗?
答案 0 :(得分:22)
我很确定,而不是100%,问题是竞争中的CompetitionAnswers映射中的Inverse()规范。 Inverse()指定子记录负责定义它们与父级的关系。大多数情况下,一对多(父母)的“一”面是对象图的“顶部”,并且“拥有”与其子女的关系。父母有孩子,关于是否保留或收养孩子的决定是父母的。然而,情况并非总是如此;一所大学可能有学生,但是有能力决定他们将去哪里的学生。在这里,学生是图表的“顶部”,学校只是识别学生出勤率的单一记录。学生可以随时转学;这是他们的决定,并没有真正以任何有意义的方式改变学校,因此学生有责任将自己视为属于学校。
你的案子是第一个:比赛有竞赛答案,孩子在逻辑上没有责任说“我属于比赛”;竞争对手“拥有”其答案集。删除Inverse()指令应该使NH将对待作为对象图的“顶部”,因此NH将插入比赛,然后是CompetitionAnswers,现在可以引用其父母的ID。
另一个与问题无关的事情,但如果你要映射到MS SQL Server数据库,并且ID列被定义为数据库中的标识列,我会为ID指定GeneratedBy.Identity()
列。 Native()
应该最终使用Identity,但它也会检查HiLo或Sequence方法是否可用。
答案 1 :(得分:5)
References(x => x.Competition)
.Column("CompetitionId")
.Not.Nullable(); //add this
并确保孩子除了父母在其集合中持有孩子之外还指向父母。当父级是反向时,这是必需的。
a1.Competition = c;
数据库端的列non null
是否在NH端映射为可空?
答案 2 :(得分:0)
您的映射没有任何问题。您的数据库可能存在问题。如果您使用Identity列作为主键,则应将CompetitionId列设置为可为空。当你有一个带有子节点的全新对象时,NHibernate的工作方式是插入父对象和子对象。然后,它使用新的父对象id更新子对象外键。