Permalink
Browse files

Can ignore intermediary subclasses in automappings

You can now ignore classes that are in the centre of an inheritance
hierarchy using the automapper. Previously if you did this, the
automapper couldn't find the link between the child and parent (because
the child's immediate parent wasn't being mapped).

Due to this change, you can just make the intermediary class abstract
and it will be ignored automatically and the hierarchy will continue to
function; otherwise, you can use the explicit IgnoreBase<T> method to
ignore non-abstract classes.

Fixes #128
  • Loading branch information...
1 parent cebca36 commit d68a3bd421c767ad0b32cc0e386dc2302195e6d7 @jagregory jagregory committed May 16, 2010
View
25 src/FluentNHibernate.Specs/Automapping/AutoPersistenceModelSpecs.cs
@@ -1,8 +1,12 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Xml;
using FluentNHibernate.Automapping;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
+using FluentNHibernate.MappingModel;
+using FluentNHibernate.MappingModel.ClassBased;
using FluentNHibernate.Specs.Automapping.Fixtures;
using Machine.Specifications;
@@ -74,4 +78,25 @@ public class when_the_automapper_is_told_to_map_an_inheritance_hierarchy
static Exception ex;
}
+
+ public class when_the_automapper_maps_an_inheritance_hierarchy_with_three_levels_and_the_middle_ignored
+ {
+ Establish context = () =>
+ mapper = AutoMap.Source(new StubTypeSource(typeof(ChildChild), typeof(Parent), typeof(Child)))
+ .IgnoreBase<Child>();
+
+ Because of = () =>
+ mappings = mapper.BuildMappings()
+ .SelectMany(x => x.Classes);
+
+ It should_map_the_parent = () =>
+ mappings.Count().ShouldEqual(1);
+
+ It should_map_the_child_child_as_a_subclass_of_parent = () =>
+ mappings.Single()
+ .Subclasses.Single().Type.ShouldEqual(typeof(ChildChild));
+
+ static AutoPersistenceModel mapper;
+ static IEnumerable<ClassMapping> mappings;
+ }
}
View
2 src/FluentNHibernate.Specs/Automapping/Fixtures/Component.cs
@@ -1,4 +1,4 @@
namespace FluentNHibernate.Specs.Automapping.Fixtures
{
- class Component {}
+ public class Component {}
}
View
18 src/FluentNHibernate.Specs/Automapping/Fixtures/Entity.cs
@@ -17,7 +17,7 @@ internal enum TestEnum {}
class EntityChild
{}
- public class A_Child : B_Parent
+ public abstract class A_Child : B_Parent
{
}
@@ -27,4 +27,20 @@ public class B_Parent
public int Id { get; set; }
public string Name { get; set; }
}
+
+ public class Child : Parent
+ {
+
+ }
+
+ public class Parent
+ {
+ public int Id { get; set; }
+ public Component Component { get; set; }
+ }
+
+ public class ChildChild : Child
+ {
+
+ }
}
View
33 src/FluentNHibernate/Automapping/AutoMapper.cs
@@ -52,10 +52,11 @@ private void MapInheritanceTree(Type classType, ClassMappingBase mapping, IList<
{
var discriminatorSet = false;
var isDiscriminated = cfg.IsDiscriminated(classType);
+ var mappingTypesWithLogicalParents = GetMappingTypesWithLogicalParents();
- foreach (var inheritedClass in mappingTypes.Where(q =>
- q.Type.BaseType == classType &&
- !cfg.IsConcreteBaseType(q.Type.BaseType)))
+ foreach (var inheritedClass in mappingTypesWithLogicalParents
+ .Where(x => x.Value != null && x.Value.Type == classType)
+ .Select(x => x.Key))
{
if (isDiscriminated && !discriminatorSet && mapping is ClassMapping)
{
@@ -93,6 +94,32 @@ private void MapInheritanceTree(Type classType, ClassMappingBase mapping, IList<
}
}
+ Dictionary<AutoMapType, AutoMapType> GetMappingTypesWithLogicalParents()
+ {
+ var excludedTypes = mappingTypes
+ .Where(x => cfg.IsConcreteBaseType(x.Type.BaseType))
+ .ToArray();
+ var availableTypes = mappingTypes.Except(excludedTypes);
+ var mappingTypesWithLogicalParents = new Dictionary<AutoMapType, AutoMapType>();
+
+ foreach (var type in availableTypes)
+ mappingTypesWithLogicalParents.Add(type, GetLogicalParent(type.Type, availableTypes));
+ return mappingTypesWithLogicalParents;
+ }
+
+ AutoMapType GetLogicalParent(Type type, IEnumerable<AutoMapType> availableTypes)
+ {
+ if (type.BaseType == typeof(object) || type.BaseType == null)
+ return null;
+
+ var baseType = availableTypes.FirstOrDefault(x => x.Type == type.BaseType);
+
+ if (baseType != null)
+ return baseType;
+
+ return GetLogicalParent(type.BaseType, availableTypes);
+ }
+
private void MapSubclass(IList<Member> mappedMembers, SubclassMapping subclass, AutoMapType inheritedClass)
{
subclass.Name = inheritedClass.Type.AssemblyQualifiedName;
View
13 src/FluentNHibernate/Automapping/AutoPersistenceModel.cs
@@ -143,12 +143,7 @@ private void CompileMappings()
if (!type.Type.IsClass || !IsNotInnerClass(type)) continue;
if (type.IsMapped) continue;
- var mapping = FindMapping(type.Type);
-
- if (mapping == null)
- AddMapping(type.Type);
- else
- MergeMap(type.Type, mapping);
+ AddMapping(type.Type);
}
autoMappingsCreated = true;
@@ -221,12 +216,6 @@ private bool ShouldMap(Type type)
return true;
}
- private void MergeMap(Type type, IMappingProvider mapping)
- {
- Type typeToMap = GetTypeToMap(type);
- autoMapper.MergeMap(typeToMap, mapping.GetClassMapping(), new List<Member>(mapping.GetIgnoredProperties()));
- }
-
public IMappingProvider FindMapping<T>()
{
return FindMapping(typeof(T));
View
10 src/FluentNHibernate/Automapping/IAutomappingConfiguration.cs
@@ -70,7 +70,17 @@ public interface IAutomappingConfiguration
bool IsDiscriminated(Type type);
string GetDiscriminatorColumn(Type type);
SubclassStrategy GetSubclassStrategy(Type type);
+
+ /// <summary>
+ /// Specifies whether an abstract type is considered a Layer Supertype
+ /// (http://martinfowler.com/eaaCatalog/layerSupertype.html). Defaults to
+ /// true for all abstract classes. Override this method if you have an
+ /// abstract class that you want mapping as a regular entity.
+ /// </summary>
+ /// <param name="type">Abstract class type</param>
+ /// <returns>Whether the type is a Layer Supertype</returns>
bool AbstractClassIsLayerSupertype(Type type);
+
string SimpleTypeCollectionValueColumn(Member member);
/// <summary>

0 comments on commit d68a3bd

Please sign in to comment.