Skip to content

Commit

Permalink
Added support for non-layer supertype abstract classes to the automap…
Browse files Browse the repository at this point in the history
…per.

You can either explicitly force an abstract type to be used with IncludeBase<T> or you can configure it in the setup with AbstractClassIsLayerSupertype.
  • Loading branch information
jagregory committed Aug 25, 2009
1 parent 1e70497 commit df4c891
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 9 deletions.
@@ -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<AbstractBase>();

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
{

}
}
Expand Up @@ -71,6 +71,7 @@
<Compile Include="..\CommonAssemblyInfo.cs">
<Link>CommonAssemblyInfo.cs</Link>
</Compile>
<Compile Include="AutoMapping\Apm\AbstractBaseClassTests.cs" />
<Compile Include="AutoMapping\Apm\AlterationCollectionTests.cs" />
<Compile Include="AutoMapping\Apm\AlterationTests.cs" />
<Compile Include="AutoMapping\Apm\AutoMappedPropertyEnumerablesTests.cs" />
Expand Down
7 changes: 6 additions & 1 deletion src/FluentNHibernate/AutoMappingExpressions.cs
Expand Up @@ -20,7 +20,12 @@ public class AutoMappingExpressions
public Func<Type, string> DiscriminatorColumn = t => "discriminator";
public Func<Type, SubclassStrategy> SubclassStrategy = t => Automapping.SubclassStrategy.JoinedSubclass;

public AutoMappingExpressions()
/// <summary>
/// Determines whether an abstract class is a layer supertype or part of a mapped inheritance hierarchy.
/// </summary>
public Func<Type, bool> AbstractClassIsLayerSupertype = t => true;

public AutoMappingExpressions()
{
IsDiscriminated = t => SubclassStrategy(t) == Automapping.SubclassStrategy.Subclass;
}
Expand Down
65 changes: 57 additions & 8 deletions src/FluentNHibernate/Automapping/AutoPersistenceModel.cs
Expand Up @@ -18,6 +18,7 @@ public class AutoPersistenceModel : PersistenceModel
private readonly AutoMappingAlterationCollection alterations = new AutoMappingAlterationCollection();
protected readonly List<InlineOverride> inlineOverrides = new List<InlineOverride>();
private readonly List<Type> ignoredTypes = new List<Type>();
private readonly List<Type> includedTypes = new List<Type>();

public AutoPersistenceModel()
{
Expand Down Expand Up @@ -62,10 +63,8 @@ public new SetupConventionFinder<AutoPersistenceModel> Conventions
}

/// <summary>
/// Setup the auto mapper
/// Alter some of the configuration options that control how the automapper works
/// </summary>
/// <param name="expressionsAction"></param>
/// <returns></returns>
public AutoPersistenceModel Setup(Action<AutoMappingExpressions> expressionsAction)
{
expressionsAction(Expressions);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -269,17 +277,58 @@ public AutoPersistenceModel OverrideAll(Action<IPropertyIgnorer> alteration)
return this;
}

/// <summary>
/// Ignore a base type. This removes it from any mapped inheritance hierarchies, good for non-abstract layer
/// supertypes.
/// </summary>
/// <typeparam name="T">Type to ignore</typeparam>
public AutoPersistenceModel IgnoreBase<T>()
{
return IgnoreBase(typeof(T));
}

/// <summary>
/// Ignore a base type. This removes it from any mapped inheritance hierarchies, good for non-abstract layer
/// supertypes.
/// </summary>
/// <param name="baseType">Type to ignore</param>
public AutoPersistenceModel IgnoreBase(Type baseType)
{
ignoredTypes.Add(baseType);
return this;
}

/// <summary>
/// Explicitly includes a type to be used as part of a mapped inheritance hierarchy.
/// </summary>
/// <remarks>
/// 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
/// <see cref="AutoMappingExpressions.AbstractClassIsLayerSupertype"/> setting.
/// </remarks>
/// <typeparam name="T">Type to include</typeparam>
public AutoPersistenceModel IncludeBase<T>()
{
return IncludeBase(typeof(T));
}

/// <summary>
/// Explicitly includes a type to be used as part of a mapped inheritance hierarchy.
/// </summary>
/// <remarks>
/// 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
/// <see cref="AutoMappingExpressions.AbstractClassIsLayerSupertype"/> setting.
/// </remarks>
/// <param name="baseType">Type to include</param>
public AutoPersistenceModel IncludeBase(Type baseType)
{
includedTypes.Add(baseType);
return this;
}

protected override string GetMappingFileName()
{
return "AutoMappings.hbm.xml";
Expand Down

0 comments on commit df4c891

Please sign in to comment.