Использование части составного первичного ключа в составном внешнем ключе в NHibernate

У нас есть довольно большая БД (~ 200 таблиц), которая почти полностью использует составные первичные ключи и составные внешние ключи, используя единственную «базовую таблицу», от которой каждая другая таблица наследует часть своего первичного ключа:

  • Родительский элемент имеет первичный ключ ParentId с одним столбцом
  • У ребенка есть составной первичный ключ (ParentId, ChildId) и внешний ключ ParentId
  • Племянник имеет составной первичный ключ (ParentId, NephewId), внешний ключ ParentId и внешний ключ (ParentId, ChildId)

и так далее. До сих пор мы управляли всей этой шебангой с помощью нашей собственной структуры ORM, но мы рассматриваем возможность использования NHibernate, который мне поручили изучить (я скачал v2.1.2).

Отображения: Ребенок

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Assembly" namespace="Namespace">
<class name="Child" table="Child">
    <composite-id name="IdChild" class="ChildId">
        <key-many-to-one name="Parent" column="ParentId" class="ParentId"></key-many-to-one>
        <key-property name="Id" column="ChildId" type="Int32"></key-property>
    </composite-id>
    <!--simple properties-->
    <set name="Nephews" table="Nephew">
        <key>
            <column name="ParentId"></column>
            <column name="ChildId"></column>
        </key>
        <one-to-many class="Nephew"/>
    </set>
</class>
</hibernate-mapping> 

Племянник

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Assembly" namespace="Namespace">
    <class name="Nephew" table="Nephew">
        <composite-id name="IdNephew" class="NephewId">
            <key-many-to-one name="Parent" column="ParentId" class="Parent"></key-many-to-one>
            <key-property name="Id" column="NephewId" type="Int32"></key-property>
        </composite-id>
        <many-to-one name="Child" class="Child">
            <column name="ParentId"></column>
            <column name="ChildId"></column>
        </many-to-one>
        <!--simple properties-->
    </class>

I can post the classes too if you want, I'll omit them now for brevity (as I'll omit the mapping for Parent, since it doesn't have problems). Every property is virtual, every mapping file is an embedded resource, every composite Id has its own class which overrides Equals and GetHashCode.

Проблема в том, что я не могу сохранить экземпляр Nephew, инициализированный с помощью простого new Nephew() и переданный _session.Save(), потому что я получаю System.IndexOutOfRangeException: Invalid index n for this SqlParameterCollection with Count=n..

Единственный столбец, который дублируется в отображении, - это ParentId. Удалив отображение many-to-one в Nephew, отображение set в Child и все связанные свойства, все работает нормально.

Я нашел несколько сообщений, сообщающих об этом исключении, и наиболее подходящим в моем случае кажется этот, из-за чего мне кажется, что моя текущая схема несовместима с NHibernate. Скажите, пожалуйста, я ошибаюсь :-)

ПРИМЕЧАНИЯ:

  • Я не использую Fluent прямо сейчас, хотя это может быть вариант, но я предпочел сначала изучить основы;
  • Да, мы понимаем, что составные первичные ключи - это большая головная боль. Эта БД за годы прошла через несколько рук, вероятно, не очень опытных, но перед ее рефакторингом мы посчитаем до 10 000

person Piddu    schedule 08.10.2010    source источник


Ответы (2)


К сожалению, вы не сможете отобразить эту взаимосвязь в ее текущей форме, как вы и сделали.

Однако есть обходной путь. Вместо сопоставления Nephew.Child как многозначного, сопоставьте ChildId как обычное свойство и используйте запрос, когда вам нужно получить дочерний элемент.

Есть еще один шанс на взлом. Если и только если Nephew.Child не является нулевым и не изменяемым, вы можете сопоставить ключ, ссылающийся на Child, а не на родительский:

<class name="Nephew" table="Nephew">
  <composite-id name="IdNephew" class="NephewId">
    <key-many-to-one name="Child">
      <column="ParentId">
      <column="ChildId">
    </key-many-to-one>
    <key-property name="Id" column="NephewId"/>
  </composite-id>
</class>
person Diego Mijelshon    schedule 08.10.2010
comment
Спасибо. Ваше первое предложение частично работает, но я теряю возможность каскадного сохранения племянников, вызывая только _session.Save (child). Мне нужно вручную создать метод Child.Add (Nephew), который добавляет племянника в коллекцию Nephews и устанавливает простое свойство ChildId в Nephew, даже с inverse = true и cascade = all в теге ‹set›. Это все еще больше, чем ничего - person Piddu; 11.10.2010

Я нашел лучшее решение:

<many-to-one name="Child" class="Child">
    <formula>ParentId</formula>
    <column name="ChildId"></column>
</many-to-one>

Я уже пробовал это, и это дало мне ошибку, но затем я заметил этот патч, поэтому я скачал 3.0.0 alpha2 и все заработало правильно! Благодаря этому решению я могу отобразить свойство Nephew.Child таким, каким оно должно было быть.

Тем не менее, мне все еще нужен метод Child.Add(Nephew) (но я понял, что он рекомендуется даже в документации)

person Piddu    schedule 13.10.2010