Skip to content

Commit 610e6a3

Browse files
committed
Support for 'fallback' derived type mapping when conditional derived type pairs don't match and target type is set by source type
1 parent d832fda commit 610e6a3

File tree

3 files changed

+79
-35
lines changed

3 files changed

+79
-35
lines changed

AgileMapper.UnitTests/WhenMappingDerivedTypes.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,22 @@ public void ShouldConditionallyMapDerivedTypesFromNestedMembers()
132132
.If((s, t) => s.Name == "Customer Mystery!")
133133
.MapTo<MysteryCustomerViewModel>();
134134

135-
var personSource = new PublicField<PersonViewModel>
135+
var mysteryCustomerSource = new PublicField<PersonViewModel>
136136
{
137137
Value = new CustomerViewModel { Name = "Mystery Customer", Discount = 0.5 }
138138
};
139-
var result = mapper.Map(personSource).ToANew<PublicProperty<PersonViewModel>>();
139+
var result = mapper.Map(mysteryCustomerSource).ToANew<PublicProperty<PersonViewModel>>();
140140

141141
result.Value.ShouldBeOfType<MysteryCustomerViewModel>();
142142
((MysteryCustomerViewModel)result.Value).Discount.ShouldBe(0.5);
143+
144+
var customerSource = new PublicField<PersonViewModel>
145+
{
146+
Value = new CustomerViewModel { Name = "Banksy" }
147+
};
148+
result = mapper.Map(customerSource).ToANew<PublicProperty<PersonViewModel>>();
149+
150+
result.Value.ShouldBeOfType<CustomerViewModel>();
143151
}
144152
}
145153
}

AgileMapper/Extensions/ExpressionExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ public static Expression GetIsNotDefaultComparisonsOrNull(this IEnumerable<Expre
6565
return allNotNullCheck;
6666
}
6767

68-
public static BinaryExpression GetIsDefaultComparison(this Expression expression)
68+
public static Expression GetIsDefaultComparison(this Expression expression)
6969
=> Expression.Equal(expression, Expression.Default(expression.Type));
7070

71-
public static BinaryExpression GetIsNotDefaultComparison(this Expression expression)
71+
public static Expression GetIsNotDefaultComparison(this Expression expression)
7272
=> Expression.NotEqual(expression, Expression.Default(expression.Type));
7373

7474
public static Expression GetToValueOrDefaultCall(this Expression nullableExpression)

AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs

Lines changed: 67 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ private static void AddDerivedSourceTypeMappings(
113113
IEnumerable<Type> derivedSourceTypes,
114114
IObjectMappingData declaredTypeMappingData,
115115
ICollection<ParameterExpression> typedObjectVariables,
116-
ICollection<Expression> typeTests)
116+
ICollection<Expression> derivedTypeMappings)
117117
{
118118
var declaredTypeMapperData = declaredTypeMappingData.MapperData;
119119

@@ -124,41 +124,83 @@ private static void AddDerivedSourceTypeMappings(
124124
var typeAsConversion = Expression.TypeAs(declaredTypeMapperData.SourceObject, derivedSourceType);
125125
var typedVariableAssignment = Expression.Assign(typedVariable, typeAsConversion);
126126

127+
typedObjectVariables.Add(typedVariable);
128+
derivedTypeMappings.Add(typedVariableAssignment);
129+
127130
var targetType = declaredTypeMapperData.TargetType.GetRuntimeTargetType(derivedSourceType);
128131

129-
var condition = GetTypePairCondition(typedVariable, derivedSourceType, ref targetType, declaredTypeMapperData);
132+
var condition = typedVariable.GetIsNotDefaultComparison();
133+
condition = AppendTargetValidCheckIfAppropriate(condition, targetType, declaredTypeMapperData);
130134

131-
var mapping = MappingFactory
132-
.GetDerivedTypeMapping(declaredTypeMappingData, typedVariable, targetType);
135+
var derivedTypePairs = GetTypePairsFor(derivedSourceType, targetType, declaredTypeMapperData);
136+
Expression ifTypePairsConditionThenMap, ifSourceVariableIsDerivedTypeThenMap;
133137

134-
var returnMappingResult = Expression.Return(declaredTypeMapperData.ReturnLabelTarget, mapping);
135-
var ifConditionThenMap = Expression.IfThen(condition, returnMappingResult);
138+
if (derivedTypePairs.None())
139+
{
140+
ifSourceVariableIsDerivedTypeThenMap = GetIfConditionThenMapExpression(
141+
declaredTypeMappingData,
142+
condition,
143+
typedVariable,
144+
targetType);
145+
146+
derivedTypeMappings.Add(ifSourceVariableIsDerivedTypeThenMap);
147+
continue;
148+
}
136149

137-
typedObjectVariables.Add(typedVariable);
138-
typeTests.Add(typedVariableAssignment);
139-
typeTests.Add(ifConditionThenMap);
150+
if (derivedTypePairs.All(tp => !tp.HasConfiguredCondition))
151+
{
152+
ifSourceVariableIsDerivedTypeThenMap = GetIfConditionThenMapExpression(
153+
declaredTypeMappingData,
154+
condition,
155+
typedVariable,
156+
derivedTypePairs[0].DerivedTargetType);
157+
158+
derivedTypeMappings.Add(ifSourceVariableIsDerivedTypeThenMap);
159+
continue;
160+
}
161+
162+
var typePairsCondition = GetTypePairsCondition(derivedTypePairs, declaredTypeMapperData);
163+
164+
ifTypePairsConditionThenMap = GetIfConditionThenMapExpression(
165+
declaredTypeMappingData,
166+
typePairsCondition,
167+
typedVariable,
168+
derivedTypePairs[0].DerivedTargetType);
169+
170+
var mapToDerivedType = GetReturnMappingResultExpression(
171+
declaredTypeMappingData,
172+
typedVariable,
173+
targetType);
174+
175+
ifSourceVariableIsDerivedTypeThenMap = Expression.IfThen(
176+
condition,
177+
Expression.Block(ifTypePairsConditionThenMap, mapToDerivedType));
178+
179+
derivedTypeMappings.Add(ifSourceVariableIsDerivedTypeThenMap);
140180
}
141181
}
142182

143-
private static Expression GetTypePairCondition(
183+
private static Expression GetIfConditionThenMapExpression(
184+
IObjectMappingData mappingData,
185+
Expression condition,
144186
Expression typedVariable,
145-
Type derivedSourceType,
146-
ref Type targetType,
147-
IMemberMapperData mapperData)
187+
Type targetType)
148188
{
149-
Expression condition = typedVariable.GetIsNotDefaultComparison();
150-
151-
var derivedTypePairs = GetTypePairsFor(derivedSourceType, targetType, mapperData);
189+
var returnMappingResult = GetReturnMappingResultExpression(mappingData, typedVariable, targetType);
190+
var ifConditionThenMap = Expression.IfThen(condition, returnMappingResult);
152191

153-
if (derivedTypePairs.Any())
154-
{
155-
targetType = derivedTypePairs[0].DerivedTargetType;
156-
condition = AppendTypePairConditionIfRequired(condition, derivedTypePairs, mapperData);
157-
}
192+
return ifConditionThenMap;
193+
}
158194

159-
condition = AppendTargetValidCheckIfAppropriate(condition, targetType, mapperData);
195+
private static Expression GetReturnMappingResultExpression(
196+
IObjectMappingData mappingData,
197+
Expression typedVariable,
198+
Type targetType)
199+
{
200+
var mapping = MappingFactory.GetDerivedTypeMapping(mappingData, typedVariable, targetType);
201+
var returnMappingResult = Expression.Return(mappingData.MapperData.ReturnLabelTarget, mapping);
160202

161-
return condition;
203+
return returnMappingResult;
162204
}
163205

164206
private static IList<DerivedTypePair> GetTypePairsFor(
@@ -181,27 +223,21 @@ private static IList<DerivedTypePair> GetTypePairsFor(
181223
return derivedTypePairs;
182224
}
183225

184-
private static Expression AppendTypePairConditionIfRequired(
185-
Expression condition,
226+
private static Expression GetTypePairsCondition(
186227
IEnumerable<DerivedTypePair> derivedTypePairs,
187228
IMemberMapperData mapperData)
188229
{
189230
var conditionalPairs = derivedTypePairs
190231
.Where(pair => pair.HasConfiguredCondition)
191232
.ToArray();
192233

193-
if (conditionalPairs.None())
194-
{
195-
return condition;
196-
}
197-
198234
var pairConditions = conditionalPairs.Skip(1).Aggregate(
199235
conditionalPairs[0].GetConditionOrNull(mapperData),
200236
(conditionSoFar, pair) => Expression.OrElse(
201237
conditionSoFar,
202238
pair.GetConditionOrNull(mapperData)));
203239

204-
return Expression.AndAlso(condition, pairConditions);
240+
return pairConditions;
205241
}
206242

207243
private static Expression AppendTargetValidCheckIfAppropriate(

0 commit comments

Comments
 (0)