Skip to content

Commit 39f0549

Browse files
authored
Merge pull request #3401 from lbargaoanu/constructors
Matching source values should override constructor parameters default…
2 parents 137a4df + d260afa commit 39f0549

File tree

9 files changed

+42
-45
lines changed

9 files changed

+42
-45
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
T:System.Runtime.CompilerServices.CompilerGeneratedAttribute
2+
T:System.Runtime.CompilerServices.IteratorStateMachineAttribute

src/AutoMapper/AutoMapper.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<PublishRepositoryUrl>true</PublishRepositoryUrl>
1919
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
2020
<BaselineAllAPICompatError>true</BaselineAllAPICompatError>
21+
<ApiCompatExcludeAttributeList>AttributeExclusions.txt</ApiCompatExcludeAttributeList>
2122
</PropertyGroup>
2223

2324
<ItemGroup>

src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace AutoMapper.Configuration
77
{
8-
public class CtorParamConfigurationExpression<TSource> : ICtorParamConfigurationExpression<TSource>, ICtorParameterConfiguration
8+
public class CtorParamConfigurationExpression<TSource, TDestination> : ICtorParamConfigurationExpression<TSource>, ICtorParameterConfiguration
99
{
1010
public string CtorParamName { get; }
1111
public Type SourceType { get; }
@@ -23,14 +23,14 @@ public void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember) =>
2323

2424
public void MapFrom<TMember>(Func<TSource, ResolutionContext, TMember> resolver)
2525
{
26-
Expression<Func<TSource, ResolutionContext, TMember>> resolverExpression = (src, ctxt) => resolver(src, ctxt);
26+
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TMember>> resolverExpression = (src, dest, destMember, ctxt) => resolver(src, ctxt);
2727
_ctorParamActions.Add(cpm => cpm.CustomMapFunction = resolverExpression);
2828
}
2929

3030
public void MapFrom(string sourceMemberName)
3131
{
3232
SourceType.GetFieldOrProperty(sourceMemberName);
33-
_ctorParamActions.Add(cpm => cpm.SourceMemberName = sourceMemberName);
33+
_ctorParamActions.Add(cpm => cpm.MapFrom(sourceMemberName));
3434
}
3535

3636
public void Configure(TypeMap typeMap)

src/AutoMapper/Configuration/MappingExpressionBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ public void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter
415415

416416
public TMappingExpression ForCtorParam(string ctorParamName, Action<ICtorParamConfigurationExpression<TSource>> paramOptions)
417417
{
418-
var ctorParamExpression = new CtorParamConfigurationExpression<TSource>(ctorParamName, SourceType);
418+
var ctorParamExpression = new CtorParamConfigurationExpression<TSource, TDestination>(ctorParamName, SourceType);
419419

420420
paramOptions(ctorParamExpression);
421421

src/AutoMapper/ConstructorParameterMap.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public ConstructorParameterMap(TypeMap typeMap, ParameterInfo parameter, IEnumer
4141
public override bool CanResolveValue { get; set; }
4242

4343
public override bool Inline { get; set; }
44-
public string SourceMemberName { get; set; }
44+
45+
public Expression DefaultValue() => Expression.Constant(Parameter.GetDefaultValue());
4546
}
4647
}

src/AutoMapper/DefaultMemberMap.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using AutoMapper.Internal;
12
using System;
23
using System.Collections.Generic;
34
using System.Linq;
@@ -6,6 +7,8 @@
67

78
namespace AutoMapper
89
{
10+
using static Expression;
11+
912
/// <summary>
1013
/// Member maps with default values. Used in dynamic/dictionary scenarios when source/destination members do not exist.
1114
/// </summary>
@@ -49,5 +52,20 @@ public MemberInfo SourceMember
4952
return SourceMembers.LastOrDefault();
5053
}
5154
}
55+
56+
public void MapFrom(LambdaExpression sourceMember)
57+
{
58+
CustomMapExpression = sourceMember;
59+
Ignored = false;
60+
}
61+
62+
public void MapFrom(string propertyOrField)
63+
{
64+
var mapExpression = TypeMap.SourceType.IsGenericTypeDefinition ?
65+
// just a placeholder so the member is mapped
66+
Lambda(Constant(null)) :
67+
ExpressionFactory.MemberAccessLambda(TypeMap.SourceType, propertyOrField);
68+
MapFrom(mapExpression);
69+
}
5270
}
5371
}

src/AutoMapper/Execution/TypeMapPlanBuilder.cs

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -320,25 +320,9 @@ private Expression CreateNewDestinationExpression(ConstructorMap constructorMap)
320320
return Block(variables, body);
321321
}
322322

323-
private Expression ResolveSource(ConstructorParameterMap ctorParamMap)
324-
{
325-
if(ctorParamMap.CustomMapExpression != null)
326-
return ctorParamMap.CustomMapExpression.ConvertReplaceParameters(Source)
327-
.NullCheck(ctorParamMap.DestinationType);
328-
if(ctorParamMap.CustomMapFunction != null)
329-
return ctorParamMap.CustomMapFunction.ConvertReplaceParameters(Source, Context);
330-
if (ctorParamMap.SourceMemberName != null)
331-
{
332-
return MemberAccesses(ctorParamMap.SourceMemberName, Source);
333-
}
334-
if (ctorParamMap.HasDefaultValue)
335-
return Constant(ctorParamMap.Parameter.GetDefaultValue());
336-
return Chain(ctorParamMap.SourceMembers, ctorParamMap.DestinationType);
337-
}
338-
339323
private Expression CreateConstructorParameterExpression(ConstructorParameterMap ctorParamMap)
340324
{
341-
var resolvedExpression = ResolveSource(ctorParamMap);
325+
var resolvedExpression = BuildValueResolverFunc(ctorParamMap, _destination, ctorParamMap.DefaultValue());
342326
var resolvedValue = Variable(resolvedExpression.Type, "resolvedValue");
343327
var tryMap = Block(new[] {resolvedValue},
344328
Assign(resolvedValue, resolvedExpression),
@@ -469,7 +453,7 @@ private Expression CreatePropertyMapFunc(IMemberMap memberMap, Expression destin
469453
return Block(new[] {resolvedValue, propertyValue}.Distinct(), mapperExpr);
470454
}
471455

472-
private Expression BuildValueResolverFunc(IMemberMap memberMap, Expression destValueExpr)
456+
private Expression BuildValueResolverFunc(IMemberMap memberMap, Expression destValueExpr, Expression defaultValue = null)
473457
{
474458
Expression valueResolverFunc;
475459
var destinationPropertyType = memberMap.DestinationType;
@@ -517,7 +501,7 @@ private Expression BuildValueResolverFunc(IMemberMap memberMap, Expression destV
517501
}
518502
else
519503
{
520-
valueResolverFunc = Throw(Constant(new Exception("I done blowed up")));
504+
valueResolverFunc = defaultValue ?? Throw(Constant(new Exception("I done blowed up")));
521505
}
522506

523507
if (memberMap.NullSubstitute != null)

src/AutoMapper/PropertyMap.cs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,6 @@ public void ApplyInheritedPropertyMap(PropertyMap inheritedMappedProperty)
105105
public bool IsResolveConfigured => ValueResolverConfig != null || CustomMapFunction != null ||
106106
CustomMapExpression != null || ValueConverterConfig != null;
107107

108-
public void MapFrom(LambdaExpression sourceMember)
109-
{
110-
CustomMapExpression = sourceMember;
111-
Ignored = false;
112-
}
113-
114-
public void MapFrom(string propertyOrField)
115-
{
116-
var mapExpression = TypeMap.SourceType.IsGenericTypeDefinition ?
117-
// just a placeholder so the member is mapped
118-
Lambda(Constant(null)) :
119-
MemberAccessLambda(TypeMap.SourceType, propertyOrField);
120-
MapFrom(mapExpression);
121-
}
122-
123108
public void AddValueTransformation(ValueTransformerConfiguration valueTransformerConfiguration) =>
124109
_valueTransformerConfigs.Add(valueTransformerConfiguration);
125110
}

src/UnitTests/Constructors.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,7 +1308,6 @@ public void Should_resolve_constructor_arguments_using_mapping_engine()
13081308
var config = new MapperConfiguration(cfg =>
13091309
{
13101310
cfg.CreateMap<SourceBar, DestinationBar>();
1311-
13121311
cfg.CreateMap<SourceFoo, DestinationFoo>();
13131312
});
13141313

@@ -1383,15 +1382,18 @@ public class When_mapping_to_an_object_with_a_constructor_with_multiple_optional
13831382
[Fact]
13841383
public void Should_resolve_constructor_when_args_are_optional()
13851384
{
1386-
1387-
var config = new MapperConfiguration(cfg => cfg.CreateMap<SourceFoo, DestinationFoo>());
1385+
var config = new MapperConfiguration(cfg =>
1386+
{
1387+
cfg.CreateMap<SourceBar, DestinationBar>();
1388+
cfg.CreateMap<SourceFoo, DestinationFoo>();
1389+
});
13881390

13891391
var sourceBar = new SourceBar("fooBar");
13901392
var sourceFoo = new SourceFoo(sourceBar);
13911393

13921394
var destinationFoo = config.CreateMapper().Map<DestinationFoo>(sourceFoo);
13931395

1394-
destinationFoo.Bar.ShouldBeNull();
1396+
destinationFoo.Bar.FooBar.ShouldBe("fooBar");
13951397
destinationFoo.Str.ShouldBe("hello");
13961398
}
13971399

@@ -1460,14 +1462,18 @@ public class When_mapping_to_an_object_with_a_constructor_with_single_optional_a
14601462
[Fact]
14611463
public void Should_resolve_constructor_when_arg_is_optional()
14621464
{
1463-
var config = new MapperConfiguration(cfg => cfg.CreateMap<SourceFoo, DestinationFoo>());
1465+
var config = new MapperConfiguration(cfg =>
1466+
{
1467+
cfg.CreateMap<SourceBar, DestinationBar>();
1468+
cfg.CreateMap<SourceFoo, DestinationFoo>();
1469+
});
14641470

14651471
var sourceBar = new SourceBar("fooBar");
14661472
var sourceFoo = new SourceFoo(sourceBar);
14671473

14681474
var destinationFoo = config.CreateMapper().Map<DestinationFoo>(sourceFoo);
14691475

1470-
destinationFoo.Bar.ShouldBeNull();
1476+
destinationFoo.Bar.FooBar.ShouldBe("fooBar");
14711477
}
14721478

14731479

0 commit comments

Comments
 (0)