如何引用与NHibernate关系的ID属性?

时间:2011-10-13 10:46:27

标签: nhibernate mapping parent-child foreign-key-relationship

如何映射关系,其中子端点通过Id属性公开,而不是通过整个Parent对象公开?

以下是示例:

class Parent {
    public Guid Id { get; set; }
    public List<Child> Chlidren { get; set; }
}

class Child {
    public Guid Id { get; set; }
    public Guid ParentId { get; set; }
}

以下是我正在使用的等效映射:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="Blabla"
                   namespace="Blabla"
                   auto-import="false">

    <typedef name="ChildrenList" class="Blabla" />

    <class name="Parent" table="Parent" lazy="false">
        <id name="Id" column="ID" type="Guid">
            <generator class="guid" />
        </id>

        <bag name="Children" table="Child"
                             cascade="save-update"
                             collection-type="ChildrenList"
                             lazy="false">
            <key column="ParentID" not-null="true" />
            <one-to-many class="Child" />
        </bag>
    </class>

    <class name="Child" table="Child" lazy="false">
        <id name="Id" column="ID" type="Guid">
            <generator class="guid" />
        </id>

        <!-- How to map ParentID here? -->
    </class>
</hibernate-mapping>

当我创建父项时,将一些子项添加到Children集合中,然后保存父项,一切都很好。但是如果首先保存父对象,那么创建一个子对象,将其ParentID属性设置为父对象的ID,然后我得到

NHibernate.PropertyValueException:
not-null property references a null or transient value Child._Parent.ChildrenBackref

在创建NHibernate配置时,所有映射many-to-one关系的尝试都会导致不同的异常。主要是关于对象类型不匹配。

我确信NHibernate能够处理这种情况。我必须要有一些相当基本的东西。

修改

我认为对示例测试有意义,因为上述例外而失败:

var child = new Child(Create.Saved<Parent>().Id); // this sets the ParentId property
this.Repository.Save(child); // here I get the exception

我的想法为什么NHibernate提出这个问题:ChildrenParent属性的映射方式表明如果没有父级(<key column="ParentID" not-null="true" />),孩子就不能存在。当我试图坚持一个孩子时,NHibernate试图解决这种关系(找到这个孩子所关联的父亲)并失败,因为在映射中没有给出子端点(否则将是ParentId属性),检查自己的Child._Parent.ChildrenBackref端点,无论它是什么。

这看起来像是一个理想的解决方案:将ParentId属性映射为关系的子端点。这将强制NHibernate通过使用ParentId属性值作为父级主键来解析父级。

事情是我不知道是否可能。

1 个答案:

答案 0 :(得分:1)

你在NHibernate中拥有的一对多/多对一关系总是需要占主导地位(即管理“保存”的一方)。

<bag name="Children" table="Child"
                     cascade="save-update"
                     collection-type="ChildrenList"
                     lazy="false">
    <key column="ParentID" not-null="true" />
    <one-to-many class="Child" />
</bag> 

以上是一对多关系,其中优势一方是父母。这意味着,您保存父...并且将首先保存父项,然后保存子项(ParentId为null),然后将发出后续更新以设置child.ParentId。

注意:

首先使用ParentId = null插入子项...如果您有一个db或映射限制,表示ParentId不能为null,则此操作将失败。

<bag name="Children" table="Child"
                     cascade="save-update"
                     collection-type="ChildrenList"
                     lazy="false"
                     inverse=true>
    <key column="ParentID" not-null="true" />
    <one-to-many class="Child" />
</bag>

请注意 inverse = true 属性。这意味着子对象在关系中占主导地位,这意味着子对象负责。将插入父级,然后将Id分配给child.ParentId,然后将子项插入并且已设置ParentId

在许多情况下,当然,你想要采取任何一种方式。最简单的方法是管理两端的关系(不幸的是,你必须自己做)。

在Parent上,你有一个方法:

public void AddChild(Child child)
{
   Children.Add(child);
   child.ParentId = Id;
}

public void RemoveChild(Child child)
{
    Children.Remove(child);
    child.ParentId = null;
}

在孩子身上,你有一个方法:

public void SetParent(Parent parent)
{
    ParentId = parent.Id;
    parent.Children.Add(this);
}

使用这些方法添加/删除/设置,执行操作后双方都是一致的。那么,无论你是否在包上设置 inverse = true 都无关紧要。

请参阅http://www.nhforge.org/doc/nh/en/index.html#collections-example