NHibernate映射子集合和嵌入的子实例

NHibernate映射子集合和嵌入的子实例,nhibernate,nhibernate-mapping,Nhibernate,Nhibernate Mapping,我试图找出映射以下父子关系的正确方法。我有一个包含子对象的父类。但是,父级也有一个指向其中一个子级(PrimaryChild)实例的指针 用法类似于 Dim ch As New Child Dim par as New Parent ch.MyParent = par par.Children.Add(ch) par.PrimaryChild = ch Session.SaveOrUpdate(par) 但是,当我这样做时,PrimaryChild显示为null或瞬态值。我已在Child

我试图找出映射以下父子关系的正确方法。我有一个包含子对象的父类。但是,父级也有一个指向其中一个子级(PrimaryChild)实例的指针

用法类似于

Dim ch As New Child
Dim par as New Parent

ch.MyParent = par
par.Children.Add(ch)

par.PrimaryChild = ch

Session.SaveOrUpdate(par)
但是,当我这样做时,PrimaryChild显示为null或瞬态值。我已在Children集合上设置了cascade=“all”

知道我做错了什么吗

更新1

添加映射

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="Parent" table="Parents">
    <id name="Id" type="System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="ID" />
    </id>

    <set access="nosetter.camelcase-underscore" cascade="all-delete-orphan" inverse="true" name="Children" mutable="true">
      <key>
        <column name="ParentID" />
      </key>
      <one-to-many class="Child" />
    </set>
    <many-to-one cascade="save-update" class="Child" name="PrimaryChild">
      <column name="PrimaryChildID" not-null="true" />
    </many-to-one>
    <many-to-one cascade="save-update" class="Child" name="SecondaryChild">
      <column name="SecondaryChildID" not-null="true" />
    </many-to-one>
  </class>
</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="Child" table="Child">
    <id name="Id" type="System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="ID" />
    </id>
    <many-to-one class="Parent" name="Parent">
      <column name="ParentID" not-null="true" />
    </many-to-one>
  </class>
</hibernate-mapping>

到目前为止,您有哪些映射

PrimaryChild
是一对一,
Children
是一对多,但您可以通过多种不同的方式管理关系。你的外键在哪里

如果将FK放在子对象上,则希望在这两个对象上都设置
inverse=true
的一对一和一对多映射

另一方面,这:

ch.MyParent = par
par.Children.Add(ch)

是一次大规模的OO封装失败<代码>父级不应公开IList,因为其他对象可以对其进行操作。父类应控制其自身的所有操作。将其设置为IEnumerable,并使用执行上述两行的
AddChild
方法。

到目前为止,您有哪些映射

PrimaryChild
是一对一,
Children
是一对多,但您可以通过多种不同的方式管理关系。你的外键在哪里

如果将FK放在子对象上,则希望在这两个对象上都设置
inverse=true
的一对一和一对多映射

另一方面,这:

ch.MyParent = par
par.Children.Add(ch)

是一次大规模的OO封装失败<代码>父级不应公开IList,因为其他对象可以对其进行操作。父类应控制其自身的所有操作。将其设置为IEnumerable,并使用执行上述两行的
AddChild
方法。

我已经使用NHibernate LINQ库等工具执行了此任务

public class Parent {
  public Parent() {
    Children = new List<Child>();
  }
  public virtual IList<Child> Children { get; set; }
  public virtual Child PrimaryChild {
      get {
        return Children.FirstOrDefault(x => x.IsPrimary);
    }
  }
}
公共类父类{
公共家长(){
Children=新列表();
}
公共虚拟IList子项{get;set;}
公共虚拟子PrimaryChild{
得到{
返回Children.FirstOrDefault(x=>x.IsPrimary);
}
}
}
如果已经加载了子项,则PrimaryChild是内存中的操作。如果您首先请求PrimaryChild,那么这将是它自己的数据库获取操作。很好,我想


你应该看看安德鲁·布洛克的回答。暴露IList会打开坏的域设计之门。您应该只公开枚举。

我已经使用NHibernate LINQ库等工具执行了此任务

public class Parent {
  public Parent() {
    Children = new List<Child>();
  }
  public virtual IList<Child> Children { get; set; }
  public virtual Child PrimaryChild {
      get {
        return Children.FirstOrDefault(x => x.IsPrimary);
    }
  }
}
公共类父类{
公共家长(){
Children=新列表();
}
公共虚拟IList子项{get;set;}
公共虚拟子PrimaryChild{
得到{
返回Children.FirstOrDefault(x=>x.IsPrimary);
}
}
}
如果已经加载了子项,则PrimaryChild是内存中的操作。如果您首先请求PrimaryChild,那么这将是它自己的数据库获取操作。很好,我想


你应该看看安德鲁·布洛克的回答。暴露IList会打开坏的域设计之门。您应该只公开枚举。

如果您的关系是双向的(即父项引用子项,子项引用父项),并且如果外键位于子项上,则需要设置属性

inverse="true"
在集合的声明中。否则,级联将无法正常工作:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="Parent">
    <id name="Id" />

    <bag name="Children" cascade="all" inverse="true">
      <key column="ID_PARENT" />
      <one-to-many class="Child"/>
    </bag>
  </class>

  <class name="Children">
    <id name="Id" />

    <many-to-one name="Parent" column="ID_PARENT" class="Parent" not-null="true" />
  </class>
</hibernate-mapping>

如果您的关系是双向的(即父项引用子项,子项引用父项),并且如果外键位于子项上,则需要设置属性

inverse="true"
在集合的声明中。否则,级联将无法正常工作:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="Parent">
    <id name="Id" />

    <bag name="Children" cascade="all" inverse="true">
      <key column="ID_PARENT" />
      <one-to-many class="Child"/>
    </bag>
  </class>

  <class name="Children">
    <id name="Id" />

    <many-to-one name="Parent" column="ID_PARENT" class="Parent" not-null="true" />
  </class>
</hibernate-mapping>

您的表格如下所示:

Table Parent
(
  ...
  PrimaryChild_FK NOT NULL
)

Table Child
(
  ...
  Paren_FK NOT NULL
)
你能告诉我插入数据的顺序吗?您既不能插入父项也不能插入子项,因为两者都需要另一个来设置外键。(NHibernate插入其中一个,并将FK设置为null,以便稍后更新。但数据库会对此进行投诉。)

从集合中删除NOTNULL约束。NHibernate不够聪明,如果您只是删除其中一个插入顺序,就无法找到一个有效的插入顺序。(AFAIK,映射文件中的NOTNULL约束实际上仅用于从中创建数据库架构)


正如mathieu已经提到的,将集合反转,并对子-父关系和父-子关系使用相同的外键。

您的表如下所示:

Table Parent
(
  ...
  PrimaryChild_FK NOT NULL
)

Table Child
(
  ...
  Paren_FK NOT NULL
)
你能告诉我插入数据的顺序吗?您既不能插入父项也不能插入子项,因为两者都需要另一个来设置外键。(NHibernate插入其中一个,并将FK设置为null,以便稍后更新。但数据库会对此进行投诉。)

从集合中删除NOTNULL约束。NHibernate不够聪明,如果您只是删除其中一个插入顺序,就无法找到一个有效的插入顺序。(AFAIK,映射文件中的NOTNULL约束实际上仅用于从中创建数据库架构)


正如mathieu已经提到的,对集合进行反转,并对子-父关系和父-子关系使用相同的外键。

@James Jarrett在这里提出了一个有用的观点,但这只是可能的优化的一个方面。这取决于您将如何访问对象图。如果您已经迫不及待地加载了,这将是最好的,否则您可以创建一个
Select N+1
问题谢谢您的回复。我不认为我可以使用这个方法,因为实际上有两个属性(PrimaryChild,SecondaryChild)可能指向同一个子实例,所以我不能使用IsPrimary@James贾雷特