Frage

This is a part of my entity-relationship model:

ER diagram

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?

War es hilfreich?

Lösung

Three-way associations in NHibernate

There are two main ways of mapping ternary (a.k.a. three-way) associations in NHibernate.

  1. map using index-many-to-many (a.k.a. map-key-many-to-many), and
  2. collection with composite-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.


collection with 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 implement Equals() and GetHashCode() 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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top