问题
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
回答1:
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>
回答2:
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)
来源:https://stackoverflow.com/questions/3892194/using-part-of-a-composite-primary-key-in-a-composite-foreign-key-in-nhibernate