Дочерняя коллекция NHibernate mapping и встроенный экземпляр child - PullRequest
1 голос
/ 20 августа 2010

Я пытаюсь выяснить, как правильно отобразить следующие родительские дочерние отношения.У меня есть родительский класс, который содержит дочерние объекты.Однако родитель также имеет указатель на экземпляр одного из дочерних элементов (PrimaryChild)

Class Parent

  Public Property Id As Integer?

  Public Property PrimaryChild As Child

  Public Property Children As IList(Of Child)

End Class

Public Class Child

    Public Property Id As Integer?

    Public MyParent As Parent

End Class

Использование - это что-то вроде

Dim ch As New Child
Dim par as New Parent

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

par.PrimaryChild = ch

Session.SaveOrUpdate(par)

Однако, когда я это делаю,PrimaryChild показывается как нулевое или временное значение.Я установил cascade = "all" в коллекции Children.

Есть идеи, что я делаю неправильно?

Обновление 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>

Ответы [ 4 ]

1 голос
/ 24 августа 2010

Ваши таблицы выглядят так:

Table Parent
(
  ...
  PrimaryChild_FK NOT NULL
)

Table Child
(
  ...
  Paren_FK NOT NULL
)

Можете ли вы сказать мне, в каком порядке должны быть вставлены данные? Вы не можете ни вставить Родителя, ни Дочернего, так как оба нуждаются в другом для установки внешнего ключа. (NHibernate вставляет один из них и устанавливает FK на ноль, чтобы обновить его позже. Но база данных жалуется.)

Удалить ненулевое ограничение из набора. NHibernate не достаточно умен, чтобы найти рабочий порядок вставки, если вы просто удалите один из них. (AFAIK, ограничения NULL в файлах сопоставления фактически используются только для создания схемы базы данных).

И, как уже упоминалось Матье, сделайте набор обратным и используйте один и тот же внешний ключ для отношений child-parent и parent-children.

0 голосов
/ 20 августа 2010

Я выполнил эту задачу с помощью таких инструментов, как библиотека 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);
    }
  }
}

Если вы уже загрузили дочерние элементы, то PrimaryChild является операцией в памяти.Если вы сначала запросите PrimaryChild, то это будет собственная операция извлечения базы данных.Хорошо работает, ИМО.

И вы должны посмотреть на ответ Эндрю Баллока.Разоблачение IList открывает путь к плохому дизайну домена.Вы должны только выставить перечисление.

0 голосов
/ 20 августа 2010

Если ваше отношение является двунаправленным (родительский элемент IE ссылается на дочерние элементы, а дочерний - на родительский), и если внешний ключ имеет значение Child, вам необходимо установить атрибут

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>
0 голосов
/ 20 августа 2010

Какие у вас есть сопоставления?

PrimaryChild - один-к-одному, Children - один-ко-многим, но вы можете управлять отношениями разными способами.Где ваши Иностранные ключи?

Если вы поместите FK на Ребенка, то вам понадобятся сопоставления как один-к-одному, так и один-ко-многим с inverse=true на обоих.* Кроме того, это:

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

является серьезной ошибкой инкапсуляции ОО.Parent не должен выставлять IList, так как другие объекты могут манипулировать им.Класс parent должен контролировать все манипуляции с собой.Сделайте его IEnumerable и используйте метод AddChild, который делает две вышеупомянутые строки.

...