在 NHibernate 中的复合外键中使用复合主键的一部分

发布于 2024-09-27 07:55:48 字数 2706 浏览 0 评论 0原文

我们有一个相当大的数据库(约 200 个表),它几乎完全使用复合主键和复合外键,使用单个“基表”,每个其他表都从中继承其主键的一部分:

  • Parent 具有单列主键 ParentId
  • Child具有复合主键(ParentId,ChildId)和外键ParentId
  • Nephew具有复合主键(ParentId,NephewId),外键ParentId和外键(ParentId,ChildId)

等。到目前为止,我们使用自己的 ORM 框架来管理整个工作,但我们正在考虑使用 NHibernate,我被分配去学习它(我已经下载了 v2.1.2)。

映射: Child

<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> 

Nephew

<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>

如果您愿意,我也可以发布这些类,为了简洁起见,我现在将省略它们(因为我将省略 Parent 的映射,因为它没有问题)。每个属性都是虚拟的,每个映射文件都是嵌入式资源,每个复合 Id 都有自己的类,该类覆盖 Equals 和 GetHashCode。

问题是我无法保存通过简单的 new Nephew() 初始化并传递给 _session.Save() 的 Nephew 实例,因为我得到了 < code>System.IndexOutOfRangeException:此 SqlParameterCollection 的索引 n 无效,且 Count=n。。

映射中唯一重复的列是ParentId。删除 Nephew 中的多对一映射、Child 中的set映射以及所有相关属性一切正常。

我发现有几篇文章报告了此异常,最适合我的情况似乎是 这个,这让我直觉我的当前架构对于NHibernate来说是不可行的。请告诉我我错了:-)

注意:

  • 我现在没有使用 Fluent,尽管它可能是一种选择,但我更喜欢先学习基础知识;
  • 是的,我们意识到复合主键是一个大麻烦。这个数据库多年来已经经过了好几手,可能不是那么熟练,但在重构它之前我们会数到 10 000

We have a fairly big DB (~200 tables) which almost entirely uses composite primary keys and composite foreign keys, using a single "base table" from which every other table inherits part of its primary key:

  • Parent has single column primary key ParentId
  • Child has composite primary key (ParentId, ChildId) and foreign key ParentId
  • Nephew has composite primary key (ParentId, NephewId), foreign key ParentId and foreign key (ParentId, ChildId)

and so on. Up until now we managed this whole shebang with an ORM framework of our own, but we're considering using NHibernate, which I've been assigned to learn (i've downloaded v2.1.2).

The mappings:
Child

<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> 

Nephew

<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.

The problem is I can't save an instance of Nephew, initialized through a simple new Nephew() and passed on to _session.Save(), because I get a System.IndexOutOfRangeException: Invalid index n for this SqlParameterCollection with Count=n..

The only column which is duplicated in the mapping is the ParentId. Removing the many-to-one mapping in the Nephew, the set mapping in the Child and all related properties everything works fine.

I found several posts reporting this exception, and the most appropriate in my case seems to be this one, which gives me the gut feeling that my current schema is infeasible with NHibernate. Please tell me I'm wrong :-)

NOTES:

  • I'm not using Fluent right now, even though it may be an option, but I preferred learning the basics first;
  • Yes, we realize composite primary keys are a big pain in the ass. This DB has passed through several hands through the years, probably not so skilled ones, but before refactoring it we will count to 10 000

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

南城旧梦 2024-10-04 07:55:48

不幸的是,正如您所推断的那样,您将无法以当前形式映射该关系。

不过,有一个解决方法。不要将 Nephew.Child 映射为多对一,而是将 ChildId 映射为常规属性,并在需要检索 Child 时使用查询。

还有一次被黑客攻击的机会。当且仅当 Nephew.Child 不为 null 且不可变时,您可以映射引用 Child 而不是parent 的键:

<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>

Unfortunately, you won't be able to map that relationship in its current form, as you have inferred.

However, there's a workaround. Instead of mapping Nephew.Child as a many-to-one, map ChildId as a regular property, and use a query when you need to retrieve the Child.

There's one more chance for a hack. If and only if Nephew.Child is not null and not mutable, you could map the key referencing the Child instead of the parent:

<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>
一梦浮鱼 2024-10-04 07:55:48

我找到了一个更好的解决方案:

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

我已经尝试过了,它给了我一个错误,但后来我注意到 这个补丁< /a>,所以我下载了 3.0.0 alpha2,一切正常!感谢这个解决方案,我可以按原样映射 Nephew.Child 属性。

不过,我仍然需要 Child.Add(Nephew) 方法(但我意识到即使在文档中也是推荐的)

I found a better solution:

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

I had already tried that and it gave me an error, but then I noticed this patch, so I downloaded the 3.0.0 alpha2 and it all worked correctly! Thanks to this solution I can map the Nephew.Child property as it was meant to be.

I still need the Child.Add(Nephew) method, though (but I realized that is recommended even in the documentation)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文