Skip to content

Commit 73fbfa1

Browse files
committed
Skipping 'do nothing' mapping of complex type child members with no mapped members or constructor parameters
1 parent 0e8fd27 commit 73fbfa1

File tree

8 files changed

+55
-7
lines changed

8 files changed

+55
-7
lines changed

AgileMapper.UnitTests/WhenViewingMappingPlans.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,16 @@ public void ShouldIncludeUnmappableStructComplexTypeMemberDetails()
303303
}
304304
}
305305

306+
[Fact]
307+
public void ShouldIncludeUnmappableNoChildDataSourcesComplexTypeMemberDetails()
308+
{
309+
string plan = Mapper
310+
.GetPlanFor(new { Int = default(int) })
311+
.ToANew<PublicField<Address>>();
312+
313+
plan.ShouldContain("No data source for Value or any of its child members");
314+
}
315+
306316
[Fact]
307317
public void ShouldShowAllCachedMappingPlans()
308318
{

AgileMapper/Members/Population/MemberPopulation.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,18 @@ public static IMemberPopulation IgnoredMember(IMemberMapperData mapperData, Conf
7575
=> CreateNullMemberPopulation(mapperData, configuredIgnore.GetIgnoreMessage);
7676

7777
public static IMemberPopulation NoDataSource(IMemberMapperData mapperData)
78-
=> CreateNullMemberPopulation(mapperData, targetMember => "No data source for " + targetMember.Name);
78+
=> CreateNullMemberPopulation(mapperData, GetNoDataSourceMessage);
79+
80+
private static string GetNoDataSourceMessage(QualifiedMember targetMember)
81+
{
82+
return targetMember.IsSimple
83+
? "No data source for " + targetMember.Name
84+
: $"No data source for {targetMember.Name} or any of its child members";
85+
}
7986

8087
private static IMemberPopulation CreateNullMemberPopulation(
8188
IMemberMapperData mapperData,
82-
Func<IQualifiedMember, string> commentFactory)
89+
Func<QualifiedMember, string> commentFactory)
8390
{
8491
return new MemberPopulation(
8592
mapperData,

AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,14 @@ private static bool MemberPopulationsExist(IEnumerable<Expression> populationsAn
9797

9898
private static Expression AddExistingTargetCheckIfAppropriate(Expression value, IObjectMappingData mappingData)
9999
{
100-
var mapperData = mappingData.MapperData;
101-
102-
if (mapperData.TargetMemberIsUserStruct() ||
103-
mapperData.TargetIsDefinitelyUnpopulated())
100+
if ((value.NodeType == ExpressionType.Default) ||
101+
mappingData.MapperData.TargetMemberIsUserStruct() ||
102+
mappingData.MapperData.TargetIsDefinitelyUnpopulated())
104103
{
105104
return value;
106105
}
107106

108-
return Expression.Coalesce(mapperData.TargetObject, value);
107+
return Expression.Coalesce(mappingData.MapperData.TargetObject, value);
109108
}
110109
}
111110
}

AgileMapper/ObjectPopulation/IObjectMapper.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace AgileObjects.AgileMapper.ObjectPopulation
55

66
internal interface IObjectMapper : IObjectMapperFunc
77
{
8+
bool IsNullObject { get; }
9+
810
Expression MappingExpression { get; }
911

1012
LambdaExpression MappingLambda { get; }

AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ public Expression Create(IObjectMappingData mappingData)
4646
mappingExpressions.AddRange(GetObjectPopulation(mappingData).WhereNotNull());
4747
mappingExpressions.AddUnlessNullOrEmpty(mappingExtras.PostMappingCallback);
4848

49+
if (NothingIsBeingMapped(mappingExpressions))
50+
{
51+
return mapperData.IsRoot ? mapperData.TargetObject : Constants.EmptyExpression;
52+
}
53+
4954
mappingExpressions.InsertRange(0, GetShortCircuitReturns(returnNull, mappingData));
5055

5156
var mappingBlock = GetMappingBlock(mappingExpressions, mappingExtras);
@@ -97,6 +102,13 @@ private static Expression GetMapToNullConditionOrNull(IMemberMapperData mapperDa
97102

98103
protected abstract IEnumerable<Expression> GetObjectPopulation(IObjectMappingData mappingData);
99104

105+
private static bool NothingIsBeingMapped(IList<Expression> mappingExpressions)
106+
{
107+
return mappingExpressions.None() ||
108+
(mappingExpressions[0].NodeType == ExpressionType.Assign) &&
109+
((BinaryExpression)mappingExpressions[0]).Right.NodeType == ExpressionType.Default;
110+
}
111+
100112
private Expression GetMappingBlock(IList<Expression> mappingExpressions, MappingExtras mappingExtras)
101113
{
102114
var mapperData = mappingExtras.MapperData;

AgileMapper/ObjectPopulation/MappingFactory.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ public static Expression GetInlineMappingBlock(
169169
{
170170
var mapper = mappingData.Mapper;
171171

172+
if (mapper.IsNullObject)
173+
{
174+
return Constants.EmptyExpression;
175+
}
176+
172177
if (mapper.MapperData.Context.UsesMappingDataObject)
173178
{
174179
return UseLocalSourceValueVariable(

AgileMapper/ObjectPopulation/ObjectMapper.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,16 @@ namespace AgileObjects.AgileMapper.ObjectPopulation
1010

1111
internal class ObjectMapper<TSource, TTarget> : IObjectMapper
1212
{
13+
public static readonly ObjectMapper<TSource, TTarget> Unmappable = new ObjectMapper<TSource, TTarget>();
14+
1315
private readonly MapperFunc<TSource, TTarget> _mapperFunc;
1416
private readonly ICache<ObjectMapperKeyBase, IObjectMapper> _subMappersByKey;
1517
private readonly ICache<ObjectMapperKeyBase, IObjectMapperFunc> _recursionMappingFuncsByKey;
1618

19+
private ObjectMapper()
20+
{
21+
}
22+
1723
public ObjectMapper(
1824
Expression<MapperFunc<TSource, TTarget>> mappingLambda,
1925
ObjectMapperData mapperData)
@@ -81,6 +87,8 @@ private ICache<ObjectMapperKeyBase, IObjectMapperFunc> CreateRecursionMapperFunc
8187

8288
public LambdaExpression MappingLambda { get; }
8389

90+
public bool IsNullObject => this == Unmappable;
91+
8492
public Expression MappingExpression => MappingLambda.Body;
8593

8694
public ObjectMapperData MapperData { get; }

AgileMapper/ObjectPopulation/ObjectMapperFactory.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public ObjectMapper<TSource, TTarget> Create<TSource, TTarget>(ObjectMappingData
5050
var mappingExpressionFactory = _mappingExpressionFactories.First(mef => mef.IsFor(mappingData));
5151
var mappingExpression = mappingExpressionFactory.Create(mappingData);
5252

53+
if (mappingExpression.NodeType == ExpressionType.Default)
54+
{
55+
return ObjectMapper<TSource, TTarget>.Unmappable;
56+
}
57+
5358
mappingExpression = MappingFactory
5459
.UseLocalSourceValueVariableIfAppropriate(mappingExpression, mappingData.MapperData);
5560

0 commit comments

Comments
 (0)