how to achieve table per concrete class when base class is abstract in fluent nhibernate?
Asked Answered
T

2

10

i have the following scenario

public abstract class BaseClass
{
  public virtual int Id {get; set};
  public virtual string Name {get; set;}
}

public class FirstSubClass : BaseClass
{
   //properties and behaviour here
}

public class SecondSubClass : BaseClass
{
  //properties of SecondSubclass Here
}

public class ProcessStep
{
   public virtual IList<BaseClass> ContentElements {get; set;}  
}

for mapping i have used following code snippet :-

this._sessionFactory =
                          Fluently.Configure().Database(SQLiteConfiguration.Standard
                          .ConnectionString(@"Data Source=SqliteTestSqlDataAccess.s3db;    Version=3; New=True; Pooling=True; Max Pool Size=1;"))
                          .Mappings(m => m.AutoMappings.Add(AutoMap.Assembly(assemblyWithDomainClasses).Conventions.Add(DefaultCascade.All())))
                          .ExposeConfiguration(BuildSchema)
                          .BuildSessionFactory();

By default fluent will ignore the abstract base class that is BaseClass. But as in the class ProcessStep there is property ContentElements which returns IList , i am getting an exception:- NHibernate.MappingException : Association references unmapped class: BaseClass

If i include the base class using the IncludeBase(typeof(BaseClass)) then it works fine but it creates a table for BaseClass and Derived classes and the records are linked with FK-PK relationship(table per subclass). What i want to achieve is table per concrete class. that is each derive class will have it's own table where there will all properties of derived class + properties in the base class. Any idea how to achieve it?

Twi answered 12/9, 2011 at 8:44 Comment(0)
M
9

Since I haven't seen your mapping, let me provide mine. You could achieve this by doing like this

public class BaseClassMap:ClassMap<BaseClass>
{
    public BaseClassMap()
    {
        /*
         * Identity generator can't be native because subclass objects should be unique
         * So use HiLo or Guid or other generators which will generate unique id on the child tables
         */
        Id(x => x.Id).GeneratedBy.Guid(); 
        Map(x => x.Name);
        UseUnionSubclassForInheritanceMapping(); // This is important - uses union-subclass mappings for the derived classes
    }
}

public class FirstSubClassMap : SubclassMap<FirstSubClass>
{
    public FirstSubClassMap()
    {
        Table("FirstSubClassTable");
        // Map properties for FirstSubClass
    }
}

public class SecondSubClassMap : SubclassMap<SecondSubClass>
{
    public SecondSubClassMap()
    {
        Table("SecondSubClassTable");
        // Map properties for SecondSubClass
    }
}
Millicentmillie answered 29/9, 2011 at 6:41 Comment(2)
i am using Automapping so , i dont have provision to map the classes separately.Twi
you can mix automapping with custom mappings, so that you can provide mapping only for specific classes, rest of the classes can be mapped using automapping. Something like this Fluently.Configure(configuration).Mappings(cfg => { cfg.AutoMappings.Add(....); cfg..FluentMappings.AddFromAssembly(Your custom mapping assembly);})Millicentmillie
F
2

It caused me headache to implement the "Table per Concrete Class" inheritance strategy with an abstract base class with nhibernate automapping. But I think, I've finally found a solution and want to share it with you. I also think, it's not added to the automapping docs, because it's maybe considered as a "weak" database design.

First here are some resources I found about this topic:

These resources basically describe how you need to do it:

  1. As you already mentioned fluent nhibernate ignores abstract base classes. So you need to add them explicitly.
// abstractBaseTypes is just a simple enumeration of base types
// model is the AutoPersistenceModel
abstractBaseTypes.ForEach(m => model = model.IncludeBase(m));
  1. a) If you know the abstract base types at compile time you can use
//sets the union subclass strategy for the known base model
model.Override<SuperType>(m => m.UseUnionSubclassForInheritanceMapping()))
  1. b) If you don't know the concrete types you can create a mapping override for each base type:
public class AbstractRightEntryMappingOverride : IAutoMappingOverride<AbstractRightEntry>
{
    public void Override(AutoMapping<AbstractRightEntry> mapping)
    {
        mapping.UseUnionSubclassForInheritanceMapping();
    }
}

// You need to tell nhibernate where to find the overriden mappings. 
// You simply can add the assemblies again.
modelAssemblies.ForEach(a => model = model.UseOverridesFromAssembly(a));
Fordham answered 17/10, 2017 at 15:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.