NHibernate在二次更新中设置外键,而不是在初始插入时,违反了键列上的NOTNULL约束

NHibernate在二次更新中设置外键,而不是在初始插入时,违反了键列上的NOTNULL约束,nhibernate,fluent-nhibernate,foreign-keys,Nhibernate,Fluent Nhibernate,Foreign Keys,我对一个相当简单(我认为)的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

我对一个相当简单(我认为)的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; }
}

这对我来说似乎很奇怪,因为几乎在所有情况下,这类外键列都被声明为不可为null,因此必须在insert时提供外键。那么为什么NHibernate不在子行的初始插入上设置外键,以及如何解决这个问题呢

映射中的一些问题。。。你有一个双向的关系,NHibernate需要知道如何更新它。在OO世界中,引用只有一种方式,NHibernate无法知道Parent->Children与Child->Parent是同一个FK。现在您将Child->Parent设置为ReadOnly()。这是告诉NHibernate不要更新此属性。因此,它尝试插入子对象(父对象为空),然后从父对象侧更新FK。如果FK上有NOTNULL约束,则此操作无效。映射这一点的通常方法是在父端使用Inverse=true,让子级担心持久性。(在OO模型中,您的工作是确保父->子集合包含与子->父关系集相同的引用集。)


James,好的,删除ReadOnly()限定符可以让测试通过。但是,现在我对ReadOnly()的含义感到困惑。我的理解是ReadOnly()意味着NHibernate在初始插入后将永远不会更新列,而不是在插入时它甚至永远不会赋值。ReadOnly()意味着在插入或更新时都不会写入值。它主要用于查找表和计算列。嗯,我知道,既然你提到了它。我想我混淆了ReadOnly()作为属性属性和ReadOnly作为实体属性。一定是老了。:)谢谢你的帮助!
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();
                });

    }
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
public class ParentMap : ClassMap<Parent>
{
    public ParentMap()
    {
        Id(x => x.ParentId).GeneratedBy.Native();
        Map(x => x.Name);
        HasMany(x => x.Children).KeyColumn("ParentId").Inverse().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").Not.Nullable();  // Removed ReadOnly()
    }
}
INSERT INTO [Parent]
           (Name)
VALUES     ('P1' /* @p0 */)
select SCOPE_IDENTITY()

INSERT INTO [Child]
           (Name,
            ParentId)
VALUES     ('C1' /* @p0 */,
            1 /* @p1 */)
select SCOPE_IDENTITY()