Skip to content

Commit 0c6fe24

Browse files
committed
Fix root cause of inheritance bug
1 parent 181a9b4 commit 0c6fe24

File tree

5 files changed

+125
-3
lines changed

5 files changed

+125
-3
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using AutoMapper;
3+
using LinkIt.AutoMapperExtensions.Config;
4+
using NUnit.Framework;
5+
6+
namespace LinkIt.AutoMapperExtensions.Tests {
7+
[TestFixture]
8+
public class IConfigurationExtensionsTests {
9+
[Test]
10+
public void Initialize_WithAbstractTransformConfig_ShouldInitializedAbstractTransformConfigFirst() {
11+
Mapper.Initialize(
12+
configuration => configuration.ApplyTransformConfigs(
13+
new[] { typeof(IConfigurationExtensionsTests).Assembly }
14+
)
15+
);
16+
17+
Mapper.AssertConfigurationIsValid();
18+
19+
//Two tests in one method because Mapper initialization is static
20+
var source = new Model{
21+
NotByConvention = new Uri("http://from-model.com/index.html")
22+
};
23+
var actual = Mapper.Map<Dto>(source);
24+
25+
Assert.That(actual.FromInterface.ToString(), Is.EqualTo("http://from-interface.com/index.html"));
26+
Assert.That(actual.FromModel.ToString(), Is.EqualTo("http://from-model.com/index.html"));
27+
}
28+
29+
public interface IDto
30+
{
31+
Uri FromInterface { get; set; }
32+
}
33+
public class Dto : IDto
34+
{
35+
public Uri FromInterface { get; set; }
36+
public Uri FromModel { get; set; }
37+
}
38+
39+
public interface IModel
40+
{
41+
}
42+
43+
public class Model:IModel
44+
{
45+
public Uri NotByConvention { get; set; }
46+
}
47+
48+
public class BBeforeA_IInterfaceConfig : IAbstractTransformConfig
49+
{
50+
public void ConfigureTransformation(IConfiguration config)
51+
{
52+
config.CreateMap<IModel, IDto>()
53+
.Include<Model, Dto>()
54+
.ForMember(
55+
dto => dto.FromInterface,
56+
member => member.UseValue(new Uri("http://from-interface.com/index.html"))
57+
);
58+
}
59+
}
60+
61+
public class ABeforeB_ModelConfig : ITransformConfig {
62+
public void ConfigureTransformation(IConfiguration config) {
63+
config.CreateMap<Model, Dto>()
64+
.ForMember(
65+
dto => dto.FromModel,
66+
member => member.MapFrom(source=>source.NotByConvention)
67+
);
68+
69+
}
70+
}
71+
72+
73+
}
74+
}

LinkIt.AutoMapperExtensions.Tests/LinkIt.AutoMapperExtensions.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
<Compile Include="AutoMapReference_PolymorphismTests.cs" />
5858
<Compile Include="AutoMapReference_PreconditionsTests.cs" />
5959
<Compile Include="Properties\AssemblyInfo.cs" />
60+
<Compile Include="IConfigurationExtensionsTests.cs" />
6061
</ItemGroup>
6162
<ItemGroup>
6263
<None Include="packages.config" />
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using AutoMapper;
2+
3+
namespace LinkIt.AutoMapperExtensions.Config
4+
{
5+
//!!! WARNING: Inherited mappings are error-prone !!!
6+
//
7+
//The problem is with
8+
//1)Inherited Explicit Mapping having a higher priority than Convention Mapping
9+
// For example, if a default is configured at the abstract level,
10+
// it is easy to forget to map the property at the implementation level.
11+
//2)Inherited Ignore Property are error-prone:
12+
// ignore property at the abstract type level should not be inherited, because it easy to
13+
// forget to map the property at the implementation level
14+
//
15+
//Mapping priorities from the highest to the lowest priority
16+
//- Explicit Mapping (using .MapFrom())
17+
//- Inherited Explicit Mapping
18+
//- Ignore Property Mapping
19+
//- Convention Mapping (Properties that are matched via convention)
20+
//- Inherited Ignore Property Mapping
21+
//Source: https://github.com/AutoMapper/AutoMapper/wiki/Mapping-inheritance
22+
//
23+
public interface IAbstractTransformConfig : ITransformConfig
24+
{
25+
}
26+
}

LinkIt.AutoMapperExtensions/Config/IConfigurationExtensions.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Reflection;
5-
using System.Text;
6-
using System.Threading.Tasks;
75
using AutoMapper;
86

97
namespace LinkIt.AutoMapperExtensions.Config {
108
public static class IConfigurationExtensions {
119

12-
public static void ApplyLoadLinkProtocolConfigs(this IConfiguration config, IEnumerable<Assembly> assemblies) {
10+
public static void ApplyTransformConfigs(this IConfiguration config, IEnumerable<Assembly> assemblies) {
1311
if (config == null) { throw new ArgumentNullException("config"); }
1412
if (assemblies == null) { throw new ArgumentNullException("assemblies"); }
1513

@@ -19,6 +17,7 @@ public static void ApplyLoadLinkProtocolConfigs(this IConfiguration config, IEnu
1917
.ToList();
2018

2119
var transformConfigs = transformConfigTypes
20+
.OrderBy(GetOrderByPriorityKey)
2221
.Select(Activator.CreateInstance)
2322
.Cast<ITransformConfig>()
2423
.ToList();
@@ -27,5 +26,26 @@ public static void ApplyLoadLinkProtocolConfigs(this IConfiguration config, IEnu
2726
transformConfig.ConfigureTransformation(config);
2827
}
2928
}
29+
30+
private static string GetOrderByPriorityKey(Type transformConfigType) {
31+
return string.Format(
32+
"{0}-{1}",
33+
GetTransformConfigPriority(transformConfigType),
34+
transformConfigType.FullName
35+
);
36+
}
37+
38+
private static int GetTransformConfigPriority(Type transformConfigType)
39+
{
40+
//IAbstractTransformConfig must be configure before ITransformConfig
41+
//because otherwise the mapping defined at the abstract level are ignored
42+
//at the implementation level.
43+
//
44+
//See WARNING: Inherited mappings are error-prone before using IAbstractTransformConfig
45+
46+
return transformConfigType.GetInterface("LinkIt.AutoMapperExtensions.Config.IAbstractTransformConfig") != null
47+
? 1
48+
: 2;
49+
}
3050
}
3151
}

LinkIt.AutoMapperExtensions/LinkIt.AutoMapperExtensions.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
</ItemGroup>
5353
<ItemGroup>
5454
<Compile Include="Config\IConfigurationExtensions.cs" />
55+
<Compile Include="Config\IAbstractTransformConfig.cs" />
5556
<Compile Include="Config\ITransformConfig.cs" />
5657
<Compile Include="LinkSourceMapper.cs" />
5758
<Compile Include="MappingExpressionExtensions.cs" />

0 commit comments

Comments
 (0)