I have a class hierarchy that conceptually looks similar to this:
That is, there's an abstract base class (Relation
) and a couple of derived classes. In practice, Customer
and Supplier
share a lot of code, so I refactored the commonoalities into an abstract class BusinessContact
. Now the actual class hierarchy looks like this:
Or in code:
public abstract class Relation
{
public virtual int Id { get; set; }
}
public class ContactPerson : Relation
{
public virtual string PhoneNumber { get; set; }
}
public abstract class BusinessContact : Relation
{
public virtual string Name { get; set; }
}
public class Customer : BusinessContact
{
public virtual string CustomerNumber { get; set; }
}
public class Supplier : BusinessContact
{
public virtual string SupplierNumber { get; set; }
}
I'd like to map this hierarchy to four tables (Relation
, ContactPerson
, Customer
and Supplier
) using joined-subclasses in NHibernate, using mapping-by-code (ModelMapper
). My mapping looks like this:
var mapper = new ModelMapper();
mapper.Class<Relation>(map =>
{
map.Id(x => x.Id, id => id.Generator(Generators.Native));
});
mapper.JoinedSubclass<ContactPerson>(map =>
{
map.Key(key => key.Column("Id"));
map.Property(x => x.PhoneNumber);
});
mapper.JoinedSubclass<Customer>(map =>
{
map.Key(key => key.Column("Id"));
map.Property(x => x.Name);
map.Property(x => x.CustomerNumber);
});
mapper.JoinedSubclass<Supplier>(map =>
{
map.Key(key => key.Column("Id"));
map.Property(x => x.Name);
map.Property(x => x.SupplierNumber);
});
However, as soon as I try to add the mapping to the Configuration I get an exception:
NHibernate.MappingException: Cannot extend unmapped class: BusinessContact
I basically understand why this happens. The generated mapping looks like this:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="JoinedSubClassMapping" assembly="JoinedSubClassMapping" xmlns="urn:nhib ernate-mapping-2.2">
<class name="Relation" abstract="true">
<id name="Id" type="Int32">
<generator class="native" />
</id>
</class>
<joined-subclass name="ContactPerson" extends="Relation">
<key column="Id" />
<property name="PhoneNumber" />
</joined-subclass>
<joined-subclass name="Customer" extends="BusinessContact">
<key column="Id" />
<property name="CustomerNumber" />
</joined-subclass>
<joined-subclass name="Supplier" extends="BusinessContact">
<key column="Id" />
<property name="SupplierNumber" />
</joined-subclass>
</hibernate-mapping>
Customer
and Supplier
define BusinessContact
in their extends
attribute, as if BusinessContact
was a "normal" entity in the model. As there's no mapping for BusinessContact
, this fails, or course. Note that the "Name" property (defined in BusinessContact
does not appear in the mapping either.
What I want the mapping to look like is this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="JoinedSubClassMapping" assembly="JoinedSubClassMapping" xmlns="urn:nhibernate-mapping-2.2">
<class name="Relation" abstract="true">
<id name="Id" type="Int32">
<generator class="native" />
</id>
</class>
<joined-subclass name="ContactPerson" extends="Relation">
<key column="Id" />
<property name="PhoneNumber" />
</joined-subclass>
<joined-subclass name="Customer" extends="Relation">
<key column="Id" />
<property name="CustomerNumber" />
<property name="Name" />
</joined-subclass>
<joined-subclass name="Supplier" extends="Relation">
<key column="Id" />
<property name="SupplierNumber" />
<property name="Name" />
</joined-subclass>
</hibernate-mapping>
That is, make Supplier
and Customer
extend Relation and include all the mapped properties of the (otherwise unmapped) BusinessContact
class.
How can I achieve this?
Here is a solution:
class MyInspector : ExplicitlyDeclaredModel {
public override bool IsEntity(Type type) {
if (type == typeof (BusinessContact))
return false;
return base.IsEntity(type);
}
}
var mapper = new ModelMapper(new MyInspector());