diff --git a/src/FluentNHibernate.Testing/AutoMapping/Apm/AbstractBaseClassTests.cs b/src/FluentNHibernate.Testing/AutoMapping/Apm/AbstractBaseClassTests.cs new file mode 100644 index 000000000..c5c4a5ae0 --- /dev/null +++ b/src/FluentNHibernate.Testing/AutoMapping/Apm/AbstractBaseClassTests.cs @@ -0,0 +1,65 @@ +using System.Linq; +using FluentNHibernate.Automapping; +using FluentNHibernate.MappingModel.Identity; +using NUnit.Framework; + +namespace FluentNHibernate.Testing.Automapping.Apm +{ + [TestFixture] + public class AbstractBaseClassTests + { + [Test] + public void ShouldBeConsideredLayerSuperTypesByDefault() + { + var automapper = + AutoMap.Source(new StubTypeSource(new[] { typeof(AbstractBase), typeof(Child) })); + + automapper.CompileMappings(); + var mappings = automapper.BuildMappings(); + + mappings + .SelectMany(x => x.Classes) + .ShouldNotContain(x => x.Type == typeof(AbstractBase)); + } + + [Test] + public void ShouldAllowSpecifyingToNotTreatAbstractsAsLayerSuperTypes() + { + var automapper = + AutoMap.Source(new StubTypeSource(new[] { typeof(AbstractBase), typeof(Child) })) + .Setup(x => x.AbstractClassIsLayerSupertype = t => false); + + automapper.CompileMappings(); + var mappings = automapper.BuildMappings(); + + mappings + .SelectMany(x => x.Classes) + .ShouldContain(x => x.Type == typeof(AbstractBase)); + } + + [Test] + public void ShouldAllowExplicitInclusionOfAnAbstractClass() + { + var automapper = + AutoMap.Source(new StubTypeSource(new[] { typeof(AbstractBase), typeof(Child) })) + .IncludeBase(); + + automapper.CompileMappings(); + var mappings = automapper.BuildMappings(); + + mappings + .SelectMany(x => x.Classes) + .ShouldContain(x => x.Type == typeof(AbstractBase)); + } + } + + public abstract class AbstractBase + { + + } + + public class Child : AbstractBase + { + + } +} \ No newline at end of file diff --git a/src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj b/src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj index 73d967e09..a9562e294 100644 --- a/src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj +++ b/src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj @@ -71,6 +71,7 @@ CommonAssemblyInfo.cs + diff --git a/src/FluentNHibernate/AutoMappingExpressions.cs b/src/FluentNHibernate/AutoMappingExpressions.cs index e880dca4c..4c34198c6 100644 --- a/src/FluentNHibernate/AutoMappingExpressions.cs +++ b/src/FluentNHibernate/AutoMappingExpressions.cs @@ -20,7 +20,12 @@ public class AutoMappingExpressions public Func DiscriminatorColumn = t => "discriminator"; public Func SubclassStrategy = t => Automapping.SubclassStrategy.JoinedSubclass; - public AutoMappingExpressions() + /// + /// Determines whether an abstract class is a layer supertype or part of a mapped inheritance hierarchy. + /// + public Func AbstractClassIsLayerSupertype = t => true; + + public AutoMappingExpressions() { IsDiscriminated = t => SubclassStrategy(t) == Automapping.SubclassStrategy.Subclass; } diff --git a/src/FluentNHibernate/Automapping/AutoPersistenceModel.cs b/src/FluentNHibernate/Automapping/AutoPersistenceModel.cs index 13637a7bc..d0dad36c4 100644 --- a/src/FluentNHibernate/Automapping/AutoPersistenceModel.cs +++ b/src/FluentNHibernate/Automapping/AutoPersistenceModel.cs @@ -18,6 +18,7 @@ public class AutoPersistenceModel : PersistenceModel private readonly AutoMappingAlterationCollection alterations = new AutoMappingAlterationCollection(); protected readonly List inlineOverrides = new List(); private readonly List ignoredTypes = new List(); + private readonly List includedTypes = new List(); public AutoPersistenceModel() { @@ -62,10 +63,8 @@ public new SetupConventionFinder Conventions } /// - /// Setup the auto mapper + /// Alter some of the configuration options that control how the automapper works /// - /// - /// public AutoPersistenceModel Setup(Action expressionsAction) { expressionsAction(Expressions); @@ -157,11 +156,20 @@ private bool ShouldMapParent(Type type) private bool ShouldMap(Type type) { - return !Expressions.IsBaseType(type) && - type != typeof(object) && - !type.IsAbstract && - !ignoredTypes.Contains(type) && - !(type.IsGenericType && ignoredTypes.Contains(type.GetGenericTypeDefinition())); + if (includedTypes.Contains(type)) + return true; // inclusions take precedence over everything + if (ignoredTypes.Contains(type)) + return false; // excluded + if (type.IsGenericType && ignoredTypes.Contains(type.GetGenericTypeDefinition())) + return false; // generic definition is excluded + if (type.IsAbstract && Expressions.AbstractClassIsLayerSupertype(type)) + return false; // is abstract and a layer supertype + if (Expressions.IsBaseType(type)) + return false; // excluded + if (type == typeof(object)) + return false; // object! + + return true; } private void MergeMap(Type type, IMappingProvider mapping) @@ -269,17 +277,58 @@ public AutoPersistenceModel OverrideAll(Action alteration) return this; } + /// + /// Ignore a base type. This removes it from any mapped inheritance hierarchies, good for non-abstract layer + /// supertypes. + /// + /// Type to ignore public AutoPersistenceModel IgnoreBase() { return IgnoreBase(typeof(T)); } + /// + /// Ignore a base type. This removes it from any mapped inheritance hierarchies, good for non-abstract layer + /// supertypes. + /// + /// Type to ignore public AutoPersistenceModel IgnoreBase(Type baseType) { ignoredTypes.Add(baseType); return this; } + /// + /// Explicitly includes a type to be used as part of a mapped inheritance hierarchy. + /// + /// + /// Abstract classes are probably what you'll be using this method with. Fluent NHibernate considers abstract + /// classes to be layer supertypes, so doesn't automatically map them as part of an inheritance hierarchy. You + /// can use this method to override that behavior for a specific type; otherwise you should consider using the + /// setting. + /// + /// Type to include + public AutoPersistenceModel IncludeBase() + { + return IncludeBase(typeof(T)); + } + + /// + /// Explicitly includes a type to be used as part of a mapped inheritance hierarchy. + /// + /// + /// Abstract classes are probably what you'll be using this method with. Fluent NHibernate considers abstract + /// classes to be layer supertypes, so doesn't automatically map them as part of an inheritance hierarchy. You + /// can use this method to override that behavior for a specific type; otherwise you should consider using the + /// setting. + /// + /// Type to include + public AutoPersistenceModel IncludeBase(Type baseType) + { + includedTypes.Add(baseType); + return this; + } + protected override string GetMappingFileName() { return "AutoMappings.hbm.xml";