• NHibernate mappings tips using an Alternative Key

    Published by on January 28th, 2013 2:11 pm under c#, DB, NHibernate, ORM

    No Comments

    Recently I’ve been working with NHibernate, and besides configuring trivial mappings, I had to deal with some not so trivial: when I wanted to link to tables using an alternative key between them. In this short post I’ll resume the necessary steps to do this in case you need to do it too (in a very easy and mechanical way).

    Below is an abstraction of the datamodel I had that time (two tables related using an alternative key in the Master):

    image

    Besides the primary key of each one of the tables (composite for Master, and not defined for Detail), you can see the relation among both tables by the AlternativeKeyQ field (by taking advantage of this unique field you don’t have to propagate the composite PK from Master to the Detail). Also the Detail table has a foreign key to another entity called ForeignKeyN in this case.

    So, how do you setup the NHibernate mappings to make it work?

    First at all you’ll need to define your domain object for Detail. Of course, you want to have a Master and a ForeignKeyN instances to allow easy navigability from Detail to both entities. So here is your base class defined in C#:

    public class Detail
    {
    public virtual Master Master { get; set; }
    public virtual ForeignKeyN ForeignKeyN { get; set; }
    }
    

    Now you have two navigation, and entity, properties in your Detail domain object. You need to define your mapping:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
    <class name="MyNamespace.Detail, MyAssembly" table="Detail" lazy="true">
    <many-to-one name="Master" column="AlternativeKeyQ" property-ref="AlternativeKeyQ" class="MyNamespace.Master, MyAssembly" />
    <many-to-one name="ForeignKeyN" column="ForeignKeyN" class="MyNamespace.ForeignKeyN, MyAssembly" />
    </class>
    </hibernate-mapping>
    

    You can see how the relation to Master is defined there, using the ‘property-ref’ attribute we let NHibernate know that we are using that field on the Master table to establish the relation. But if you try to parse this NHibernate mapping within a session you’ll receive an error saying that the entity doesn’t contain an identifier (basically).

    So we’re going to fix this by adding an identifier to the entity, as we need to add this field to both (class and mapping) we are going to define it in a way that only NHibernate makes use of it:

    public class Detail
    {
    public virtual Master Master { get; set; }
    public virtual ForeignKeyN ForeignKeyN { get; set; }
    protected virtual int Id
    {
    get { return this.Master.AlternativeKey; }
    set { // do nothing.. }
    }
    }
    

    Now we added a protected field to the Detail class that we are going to use as main identifier within the NHibernate mapping (but don’t expose it in the object’s interface – i.e. it won’t be visible to users).

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
    <class name="MyNamespace.Detail, MyAssembly" table="Detail" lazy="true">
    <id name="Id" column="AlternativeKeyQ" />
    <many-to-one name="Master" column="AlternativeKeyQ" property-ref="AlternativeKeyQ" class="MyNamespace.Master, MyAssembly" insert="false" update="false" />
    <many-to-one name="ForeignKeyN" column="ForeignKeyN" class="MyNamespace.ForeignKeyN, MyAssembly" />
    </class>
    </hibernate-mapping>
    

    As you can see in the mapping, we are using the Id property of the class as the identifier, and mapping it to the AlternativeKeyQ table field. We also disabled insertion or edition (insert=”false” – update=”false”) via the many-to-one Master property as otherwise it will clash with the Id update when NHibernate tries to update the entity.

    In the Master mapping to add a bag with the Detail entities you have to use again the ‘property-ref’ attribute to let NHibernate know that you are linking these entities by AlternativeKeyQ field in the Master (if NHibernate requires you to override Equals() and GetHashCode() within your Detail domain object, use your new Id property to compare and return hash values).

    Tags: , , , , ,