This is a part of my entity-relationship model:
I found few ways to map the 'It' relationship:
One: Add new class, and then reference it using a many to many relatinship:
class It{
A a;
B b;
C c;
}
class A{
public virtual ISet<It> Relationship{set;get;}
}
However, I fear NHibernate might add an ID to 'It' on its own, or I that I might have to use a composite key (which isn't a good practice).
Two: I found this. Which uses two separate collections on each class to model a three way relationship. But I think there will be an unnecessary effort to access the information later, plus it's a bit trickier to get the mapping right.
Instead, I figured I could do something like:
class A{
IList<Tuple<C, D>> ARelationship;
}
That way you have the two references together from the beginning. But I haven't been able to find an example on how to do such mapping (a list of tuples). I found you could define an ICompositeUserType subclass to do that, however I don't fully understand how that works or how to use it to solve my problem.
My question(s) being: What's the best way to model such a relationship?
If it's by using a separate class (method one), what's the best way to map it from the viewpoint of each class, and how to map the newly defined class?
If it's by using a list of tuples, is it possible to map it without using ICompositeUserType? How?
Also, I might have missed the answer by doing incorrect Google searches. Are there other options?
There are two main ways of mapping ternary (a.k.a. three-way) associations in NHibernate.
map
using index-many-to-many
(a.k.a. map-key-many-to-many
), andcomposite-element
contents.map
using index-many-to-many
From the documentation:
There are two possible approaches to mapping a ternary association. One approach is to use composite elements (discussed below). Another is to use an
IDictionary
with an association as its index:<map name="Contracts" lazy="true"> <key column="employer_id"/> <index-many-to-many column="employee_id" class="Employee"/> <one-to-many class="Contract"/> </map> <map name="Connections" lazy="true"> <key column="node1_id"/> <index-many-to-many column="node2_id" class="Node"/> <many-to-many column="connection_id" class="Connection"/> </map>
When possible, I enjoy using the IDictionary
approach pictured above. This approach is a good fit when there is some kind of unique constraint on two of the references in the association table, like so:
create table It (
A_id int not null,
B_id int not null,
C_id int not null,
primary key (A_id, B_id),
foreign key (A_id) references A (Id),
foreign key (B_id) references B (Id),
foreign key (C_id) references C (Id)
);
This allows you to create an object model that looks like:
public class A
{
public virtual IDictionary<B, C> RelatedCEntities { get; set; }
}
The mapping for this would look like:
<map name="RelatedCEntities" table="It">
<key column="A_id"/>
<index-many-to-many column="B_id" class="B"/>
<many-to-many column="C_id" class="C"/>
</map>
You never end up having to map It
as an entity. It's simply used as the association table for the many-to-many
.
composite-element
If there is no unique constraint, we can look a little lower in the documentation for composite elements:
Collections of components are supported (eg. an array of type
Name
). Declare your component collection by replacing the<element>
tag with a<composite-element>
tag.<set name="SomeNames" table="some_names" lazy="true"> <key column="id"/> <composite-element class="Eg.Name, Eg"> <!-- class attribute required --> <property name="Initial"/> <property name="First"/> <property name="Last"/> </composite-element> </set>
Note: if you define an
ISet
of composite elements, it is very important to implementEquals()
andGetHashCode()
correctly....
Even ternary (or quaternary, etc) associations are possible:
<class name="Order" .... > .... <set name="PurchasedItems" table="purchase_items" lazy="true"> <key column="order_id"> <composite-element class="OrderLine"> <many-to-one name="PurchaseDetails class="Purchase"/> <many-to-one name="Item" class="Item"/> </composite-element> </set> </class>
I don't think you'll be able to use Tuple<T1, T2>
because it is immutable - properties Item1
and Item2
have no setters. But it is trivial to create a small class with two properties for this purpose.