我对一个相当简单(我认为)的 NHibernate 用例有疑问。
我有一个经典的父实体和子实体,如下所示:
public class Parent
{
public virtual int ParentId { get; set; }
public virtual string Name { get; set; }
public virtual IList<Child> Children { get; set; }
}
public class Child
{
public virtual int ChildId { get; set; }
public virtual Parent Parent { get; set; }
public virtual string Name { get; set; }
}
以及映射如下:
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
Id(x => x.ParentId).GeneratedBy.Native();
Map(x => x.Name);
HasMany(x => x.Children).KeyColumn("ParentId").Cascade.SaveUpdate();
}
}
public class ChildMap : ClassMap<Child>
{
public ChildMap()
{
Id(x => x.ChildId).GeneratedBy.Native();
Map(x => x.Name);
References(x => x.Parent).Column("ParentId").ReadOnly().Not.Nullable();
}
}
最后,我有一个简单的测试:
[Test]
public void Test_save_family()
{
var parent = new Parent();
var child = new Child {Parent = parent};
parent.Children = new List<Child>{child};
SessionManager.WithSession(
session =>
{
session.Save(parent);
session.Flush();
});
}
测试失败并出现 System.Data.SqlClient.SqlException :无法将 NULL 值插入列“ParentId”中。这是正确的,因为该列不可为空,但为什么要插入空值?
如果我删除 null 约束,则保存会起作用,因为 NHibernate 首先插入父记录,然后插入子记录,然后更新子记录上的 ParentId 列,如以下输出所示:
NHibernate: INSERT INTO [Parent] (Name) VALUES (@p0); select SCOPE_IDENTITY();@p0 = NULL
NHibernate: INSERT INTO [Child] (Name) VALUES (@p0); select SCOPE_IDENTITY();@p0 = NULL
NHibernate: UPDATE [Child] SET ParentId = @p0 WHERE ChildId = @p1;@p0 = 2, @p1 = 1
这对我来说似乎很奇怪,因为在几乎所有情况下,此类外键列都被声明为不可为空,因此必须在插入时提供外键。那么为什么 NHibernate 没有在子行的初始插入上设置外键以及如何解决这个问题?