Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/Mapping-inheritance.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ CreateMap<DerivedEntity, DerivedDto>()

In each case above, the derived mapping inherits the custom mapping configuration from the base mapping configuration.

To include all derived maps, from the base type map configuration:

```c#
CreateMap<BaseEntity, BaseDto>()
.IncludeAllDerived();

CreateMap<DerivedEntity, DerivedDto>();
```

### Runtime polymorphism

Take:
Expand Down
8 changes: 8 additions & 0 deletions src/AutoMapper/Configuration/MappingExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public IMappingExpression ForMember(string name, Action<IMemberConfigurationExpr

public new IMappingExpression PreserveReferences() => (IMappingExpression)base.PreserveReferences();

public new IMappingExpression IncludeAllDerived() => (IMappingExpression) base.IncludeAllDerived();

protected override IPropertyMapConfiguration CreateMemberConfigurationExpression<TMember>(MemberInfo member, Type sourceType)
=> new MemberConfigurationExpression(member, sourceType);

Expand Down Expand Up @@ -532,6 +534,12 @@ public IMappingExpression<TSource, TDestination> ValidateMemberList(MemberList m
});
return this;
}

public IMappingExpression<TSource, TDestination> IncludeAllDerived()
{
TypeMapActions.Add(tm => tm.IncludeAllDerivedTypes = true);
return this;
}

private IPropertyMapConfiguration GetDestinationMemberConfiguration(MemberInfo destinationMember) =>
_memberConfigurations.FirstOrDefault(m => m.DestinationMember == destinationMember);
Expand Down
11 changes: 11 additions & 0 deletions src/AutoMapper/IMappingExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ IMappingExpression AfterMap<TMappingAction>()
/// <param name="memberList">Member list to validate</param>
/// <returns>Itself</returns>
IMappingExpression ValidateMemberList(MemberList memberList);

/// <summary>
/// Include this configuration in all derived types' maps. Works by scanning all type maps for matches during configuration.
/// </summary>
/// <returns>Itself</returns>
IMappingExpression IncludeAllDerived();
}

/// <summary>
Expand Down Expand Up @@ -444,5 +450,10 @@ IMappingExpression<TSource, TDestination> ForSourceMember(string sourceMemberNam
/// <returns>Itself</returns>
IMappingExpression<TSource, TDestination> ValidateMemberList(MemberList memberList);

/// <summary>
/// Include this configuration in all derived types' maps. Works by scanning all type maps for matches during configuration.
/// </summary>
/// <returns>Itself</returns>
IMappingExpression<TSource, TDestination> IncludeAllDerived();
}
}
13 changes: 13 additions & 0 deletions src/AutoMapper/MapperConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,19 @@ private void Seal()
profile.Register(this);
}

foreach (var typeMap in _typeMapRegistry.Values.Where(tm => tm.IncludeAllDerivedTypes))
{
foreach (var derivedMap in _typeMapRegistry
.Where(tm =>
typeMap.SourceType.IsAssignableFrom(tm.Key.SourceType) &&
typeMap.DestinationType.IsAssignableFrom(tm.Key.DestinationType) &&
typeMap != tm.Value)
.Select(tm => tm.Value))
{
typeMap.IncludeDerivedTypes(derivedMap.SourceType, derivedMap.DestinationType);
}
}

foreach (var profile in Profiles)
{
profile.Configure(this);
Expand Down
2 changes: 2 additions & 0 deletions src/AutoMapper/TypeMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ public PathMap FindPathMapByDestinationPath(string destinationFullPath) =>

public bool ConstructDestinationUsingServiceLocator { get; set; }

public bool IncludeAllDerivedTypes { get; set; }

public MemberList ConfiguredMemberList { get; set; }

public IEnumerable<TypePair> IncludedDerivedTypes => _includedDerivedTypes;
Expand Down
45 changes: 45 additions & 0 deletions src/UnitTests/MappingInheritance/IncludeAllDerived.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Shouldly;
using Xunit;

namespace AutoMapper.UnitTests.MappingInheritance
{
public class IncludeAllDerived : AutoMapperSpecBase
{
public class A
{
public int Value { get; set; }
}
public class B : A { }
public class C : B { }
public class D : A { }

public class ADto
{
public int Value { get; set; }
}

public class BDto : ADto { }
public class CDto : BDto { }
public class DDto : ADto { }

protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg =>
{
cfg.CreateMap<A, ADto>()
.ForMember(d => d.Value, opt => opt.MapFrom(src => 5))
.IncludeAllDerived();

cfg.CreateMap<B, BDto>();
cfg.CreateMap<C, CDto>();
cfg.CreateMap<D, DDto>();
});

[Fact]
public void Should_apply_configuration_to_all_derived()
{
Mapper.Map<ADto>(new A {Value = 10}).Value.ShouldBe(5);
Mapper.Map<BDto>(new B {Value = 10}).Value.ShouldBe(5);
Mapper.Map<CDto>(new C {Value = 10}).Value.ShouldBe(5);
Mapper.Map<DDto>(new D {Value = 10}).Value.ShouldBe(5);
}
}
}