Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance Mapping with Fluent NHibernate

People also ask

What is the difference between NHibernate and fluent NHibernate?

Fluent NHibernate offers an alternative to NHibernate's standard XML mapping files. Rather than writing XML documents, you write mappings in strongly typed C# code. This allows for easy refactoring, improved readability and more concise code.

What inheritance mapping strategies are available in hibernate?

Hibernate supports the three basic inheritance mapping strategies: table per class hierarchy. table per subclass. table per concrete class.

Which hierarchy mapping will you use when polymorphic queries or associations are needed?

You can use JPA's standard association mappings to reference another concrete entity class or reference an inheritance hierarchy. But JPA can't model an association to multiple independent entity classes. If you need such an association, you can use Hibernate's @Any and @ManyToAny association mapping.


I know this is really old, but it is now pretty simple to set up fluent to generate the exact mapping you initially desired. Since I came across this post when searching for the answer, I thought I'd post it.

You just create your ClassMap for the base class without any reference to your subclasses:

public class ItemMap : ClassMap<Item>
{
    public ItemMap()
    {
        this.Table("Item");
        this.DiscriminateSubClassesOnColumn("ItemType");
        this.Id(x => x.ItemId, "ItemId");
        this.Map(x => x.FieldA, "FieldA");
    }
}

Then map your abstract subclass like this:

public class SubItemMap: SubclassMap<SubItemMap>
{
    public SubItemMap()
    {
        this.Map(x => x.FieldB);
    }
}

Then map your concrete subclasses like so:

public class ConcreteItemXMap : SubclassMap<ConcreteItemX>
{
    public ConcretItemXMap()
    {
        this.Join("ConcreteItemX", x =>
        {
            x.KeyColumn("ItemID");
            x.Map("FieldC")
        });
    }
}

Hopefully this helps somebody else looking for this type of mapping with fluent.


Well, I'm not sure that it's quite right, but it might work... If anyone can do this more cleanly, I'd love to see it (seriously, I would; this is an interesting problem).

Using the exact class definitions you gave, here are the mappings:

public class ItemMap : ClassMap<Item>
{
    public ItemMap()
    {
        Id(x => x.ItemId);
        Map(x => x.ItemType);
        Map(x => x.FieldA);

        AddPart(new ConcreteItemYMap());
    }
}

public class SubItemMap : ClassMap<SubItem>
{
    public SubItemMap()
    {
        WithTable("Item");

        // Get the base map and "inherit" the mapping parts
        ItemMap baseMap = new ItemMap();
        foreach (IMappingPart part in baseMap.Parts)
        {
            // Skip any sub class parts... yes this is ugly
            // Side note to anyone reading this that might know:
            // Can you use GetType().IsSubClassOf($GenericClass$)
            // without actually specifying the generic argument such
            // that it will return true for all subclasses, regardless
            // of the generic type?
            if (part.GetType().BaseType.Name == "JoinedSubClassPart`1")
                continue;
            AddPart(part);
        }
        Map(x => x.FieldB);
        AddPart(new ConcreteItemXMap());
    }
}

public class ConcreteItemXMap : JoinedSubClassPart<ConcreteItemX>
{
    public ConcreteItemXMap()
        : base("ItemId")
    {
        WithTableName("ConcreteItemX");
        Map(x => x.FieldC);
    }
}

public class ConcreteItemYMap : JoinedSubClassPart<ConcreteItemY>
{
    public ConcreteItemYMap()
        : base("ItemId")
    {
        WithTableName("ConcreteItemY");
        Map(x => x.FieldD);
    }
}

Those mappings produce two hbm.xml files like so (some extraneous data removed for clarity):

  <class name="Item" table="`Item`">
    <id name="ItemId" column="ItemId" type="Int32">
      <generator class="identity" />
    </id>
    <property name="FieldA" type="String">
      <column name="FieldA" />
    </property>
    <property name="ItemType" type="String">
      <column name="ItemType" />
    </property>
    <joined-subclass name="ConcreteItemY" table="ConcreteItemY">
      <key column="ItemId" />
      <property name="FieldD">
        <column name="FieldD" />
      </property>
    </joined-subclass>
  </class>

  <class name="SubItem" table="Item">
    <id name="ItemId" column="ItemId" type="Int32">
      <generator class="identity" />
    </id>
    <property name="FieldB" type="String">
      <column name="FieldB" />
    </property>
    <property name="ItemType" type="String">
      <column name="ItemType" />
    </property>
    <property name="FieldA" type="String">
      <column name="FieldA" />
    </property>
    <joined-subclass name="ConcreteItemX" table="ConcreteItemX">
      <key column="ItemId" />
      <property name="FieldC">
        <column name="FieldC" />
      </property>
    </joined-subclass>
  </class>

It's ugly, but it looks like it might generate a usable mapping file and it's Fluent! :/ You might be able to tweak the idea some more to get exactly what you want.


This is how I resolved my inheritance problem:

public static class DataObjectBaseExtension
{
    public static void DefaultMap<T>(this ClassMap<T> DDL) where T : IUserAuditable 
    {
        DDL.Map(p => p.AddedUser).Column("AddedUser");
        DDL.Map(p => p.UpdatedUser).Column("UpdatedUser");
    }
}

You can then add this to your superclass map constructor:

internal class PatientMap : ClassMap<Patient>
{
    public PatientMap()
    {
        Id(p => p.GUID).Column("GUID");
        Map(p => p.LocalIdentifier).Not.Nullable();
        Map(p => p.DateOfBirth).Not.Nullable();
        References(p => p.Sex).Column("RVSexGUID");
        References(p => p.Ethnicity).Column("RVEthnicityGUID");

        this.DefaultMap();
    }


}

The line of code: if (part.GetType().BaseType.Name == "JoinedSubClassPart1") can be rewritten as follows:

part.GetType().BaseType.IsGenericType && part.GetType().BaseType.GetGenericTypeDefinition() == typeof(JoinedSubClassPart<>)