简体   繁体   中英

Fluent NHibernate table-per-inheritance (TPH) mapping for multi class in the hierarchy

Hello everyone,

I'm struggling with one Fluent NHibernate issue. I have following template of class structure in the my solution:

class OneClass
{
    public virtual string OneProp {get; set;}
}

class TwoClass : OneClass
{
     public virtual string TwoProp {get; set;}
}

class ThreeClass : TwoClass
{
     public virtual string ThreeProp {get; set;}
}

And I want to use table-per-inheritance hierarchy strategy for my classes for contains all data in one table in a database.

How it possible to do via Fluent NHibernate?

I have tried following cases:

1. I have added discriminator for parent class

 public class OneClassMappingOverride : IAutoMappingOverride<OneClass>
 {
        public void Override(AutoMapping<OneClass> mapping)
        {
            mapping.DiscriminateSubClassesOnColumn("Type");
            mapping.SubClass<OneClass>("OneClass");
            mapping.SubClass<TwoClass>("TwoClass");
            mapping.SubClass<ThreeClass>("ThreeClass");
        }
 }

But I have got the exception: (XmlDocument)(56,8): XML validation error: The element 'subclass' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'joined-subclass' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'meta, tuplizer, synchronize, property, many-to-one, one-to-one, component, dynamic-component, properties, any, map, set, list, bag, idbag, array, primitive-array, join, subclass, loader, sql-insert, sql-update, sql-delete, resultset, query, sql-query' in namespace 'urn:nhibernate-mapping-2.2'.

because I have following auto generated xml mapping for NHibernate:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="MyClass.Domain.OneClass, MyClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`OneClass`">
    <cache usage="read-write" />
    <id name="PersistenceId" type="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="Systematic.Persistence.NHibernate.NHibernateIdGenerator, Systematic.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </id>
    <discriminator type="String">
      <column name="Type" />
    </discriminator>
    <version generated="never" name="PersistedVersion" type="System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" unsaved-value="0">
      <column name="PersistedVersion" />
    </version>
    <property name="OneProp" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="OneProp" length="255" />
    </property>
    <property name="DisplayName" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="DisplayName" length="256" index="idx__DisplayName" />
    </property>
    <property name="SystemName" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="SystemName" length="256" index="idx__SystemName" not-null="true" />
    </property>
    <property name="Version" type="System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Version" not-null="true" />
    </property>
    <property name="Description" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Description" length="10000000" />
    </property>
    <many-to-one class="Systematic.Persistence.PersistenceInfo, Systematic.Api, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" fetch="join" lazy="false" name="Persistence">
      <column name="Persistence_id" index="idx__Persistence" not-null="true" />
    </many-to-one>
    <subclass name="MyClass.Domain.TwoClass, MyClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <property name="TwoProp" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="TwoProp" length="255" />
      </property>
      <joined-subclass name="MyClass.Domain.ThreeClass, MyClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <key>
          <column name="TwoClass_id" />
        </key>
        <property name="ThreeProp" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="ThreeProp" length="255" />
        </property>
      </joined-subclass>
    </subclass>
  </class>
</hibernate-mapping>

2. I have tried to add different discriminators

public class OneClassMappingOverride : IAutoMappingOverride<OneClass>
 {
        public void Override(AutoMapping<OneClass> mapping)
        {
            mapping.DiscriminateSubClassesOnColumn("OneType");
        }
 }

 public class TwoClassMappingOverride : IAutoMappingOverride<TwoClass>
 {
        public void Override(AutoMapping<TwoClass> mapping)
        {
            mapping.DiscriminateSubClassesOnColumn("TwoType");
        }
 }

But without any results from system (same exception and mapping file)

3. I have tried to use ClassMap<> and SubclassMap<>

public class OneClassMap : ClassMap<OneClass>
{
    public OneClassMap()
    {
        DiscriminateSubClassesOnColumn("Type");

        Id(x => x.Id);

        Map(x => x.OneProp);
    }
}

public class TwoClassMap : SubclassMap<TwoClass>
{
    public TwoClassMap()
    {
        DiscriminatorValue("TwoType");

        Map(x => x.TwoProp);
    }
}

public class ThreeClassMap : SubclassMap<ThreeClass>
{
    public ThreeClassMap()
    {
        DiscriminatorValue("ThreeType");

        Map(x => x.ThreProp);
    }
}

In this case I have three tables (OneClass, TwoClass and ThreeClass in the my DB) also I have got following xml mapping file:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="MyClass.Domain.OneClass, MyClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`OneClass`">
    <cache usage="read-write" />
    <id name="PersistenceId" type="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="Systematic.Persistence.NHibernate.NHibernateIdGenerator, Systematic.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </id>
    <version generated="never" name="PersistedVersion" type="System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" unsaved-value="0">
      <column name="PersistedVersion" />
    </version>
    <property name="OneProp" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="OneProp" length="255" />
    </property>
    <property name="DisplayName" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="DisplayName" length="256" index="idx__DisplayName" />
    </property>
    <property name="SystemName" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="SystemName" length="256" index="idx__SystemName" not-null="true" />
    </property>
    <property name="Version" type="System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Version" not-null="true" />
    </property>
    <property name="Description" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Description" length="10000000" />
    </property>
    <many-to-one class="Systematic.Persistence.PersistenceInfo, Systematic.Api, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" fetch="join" lazy="false" name="Persistence">
      <column name="Persistence_id" index="idx__Persistence" not-null="true" />
    </many-to-one>
    <joined-subclass name="MyClass.Domain.TwoClass, MyClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <key>
        <column name="OneClass_id" />
      </key>
      <property name="TwoProp" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="TwoProp" length="255" />
      </property>
      <joined-subclass name="MyClass.Domain.ThreeClass, MyClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <key>
          <column name="TwoClass_id" />
        </key>
        <property name="ThreeProp" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <column name="ThreeProp" length="255" />
        </property>
      </joined-subclass>
    </joined-subclass>
  </class>
</hibernate-mapping>

I don't know how can I solve the my problem. Maybe someone give me a hand with this issue.

Many thanks.

Sincerely, Alexey

I have tried to address a similar problem. Also in my case, I have a class in one assembly and want to inherit a class in a different assembly from this non-abstract base. Fluent NHibernate will just not map the second (inherited) class. As the posts above describe, it seems only possible to map inheritance hierarchies when

  1. Only abstract classes are in different assemblies and
  2. All non-abstract classes from the inheritance hierarchy are in the same assembly

In my opinion, this is a very severe design limitation for domain modelling. Can someone close to the Fluent NHibernate project confirm that it is not possible to inherit from non-abstract classes in different assemblies? Or does anyone have a solution for addressing this problem?

Thanks, Roland.

I believe that the problem in your case could be caused by different classes in the hierarchy being placed in different assemblies. I also faced this issue and looks like such case is not supported by Fluent.

I found the exactly my situation here , and they fixed it by making the parent class abstract. Still struggling to find out if there is some other way or official statement that it's not supported, but unsuccessful yet.

Sorry, it is late. But better late than never.

Ideally the solution which you've tried should work without issues. But, unfortunately, FNH does not support it. To make it work you need to implement IAutomappingConfiguration and override IsDiscriminated method. Something like this:

class MyDefaultConfiguration : DefaultAutomappingConfiguration
{
    private readonly Type[] discriminatedTypes = new[]
    {
        typeof(OneClass), typeof(TwoClass), typeof(ThreeClass)
    };

    public override bool IsDiscriminated(Type type)
    {
        return discriminatedTypes.Contains(type) || base.IsDiscriminated(type);
    }
}

Then you can use this configuration in when you create your persistent model:

AutoMap.AssemblyOf<OneClass>(new MyDefaultConfiguration())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM