Skip to content

Commit c9928a2

Browse files
authored
Merge pull request #3413 from AutoMapper/forallmaps
Enable ForAllMaps to work with ForCtorParam
2 parents 2bed007 + 57d7c66 commit c9928a2

File tree

4 files changed

+90
-65
lines changed

4 files changed

+90
-65
lines changed

src/AutoMapper/ApiCompatBaseline.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Object AutoMap
2525
InterfacesShouldHaveSameMembers : Interface member 'public TDestination AutoMapper.IRuntimeMapper.Map<TDestination>(System.Object, System.Action<AutoMapper.IMappingOperationOptions>)' is present in the implementation but not in the contract.
2626
InterfacesShouldHaveSameMembers : Interface member 'public TDestination AutoMapper.IRuntimeMapper.Map<TSource, TDestination>(TSource, System.Action<AutoMapper.IMappingOperationOptions<TSource, TDestination>>)' is present in the implementation but not in the contract.
2727
InterfacesShouldHaveSameMembers : Interface member 'public TDestination AutoMapper.IRuntimeMapper.Map<TSource, TDestination>(TSource, TDestination, System.Action<AutoMapper.IMappingOperationOptions<TSource, TDestination>>)' is present in the implementation but not in the contract.
28-
CannotChangeAttribute : Attribute 'System.Runtime.CompilerServices.IteratorStateMachineAttribute' on 'AutoMapper.MapperConfiguration.GetIncludedTypeMaps(System.Collections.Generic.IEnumerable<AutoMapper.TypePair>)' changed from '[IteratorStateMachineAttribute(typeof(MapperConfiguration.<GetIncludedTypeMaps>d__69))]' in the contract to '[IteratorStateMachineAttribute(typeof(MapperConfiguration.<GetIncludedTypeMaps>d__72))]' in the implementation.
2928
TypesMustExist : Type 'AutoMapper.MemberFinderVisitor' does not exist in the implementation but it does exist in the contract.
3029
MembersMustExist : Member 'public System.Boolean AutoMapper.ProfileMap.MapDestinationCtorToSource(AutoMapper.TypeMap, System.Reflection.ConstructorInfo, AutoMapper.TypeDetails, System.Collections.Generic.List<AutoMapper.Configuration.ICtorParameterConfiguration>)' does not exist in the implementation but it does exist in the contract.
3130
MembersMustExist : Member 'public System.Boolean AutoMapper.ProfileMap.MapDestinationPropertyToSource(AutoMapper.TypeDetails, System.Type, System.Type, System.String, System.Collections.Generic.LinkedList<System.Reflection.MemberInfo>)' does not exist in the implementation but it does exist in the contract.
@@ -35,7 +34,7 @@ MembersMustExist : Member 'public void AutoMapper.PropertyMap.UseDestinationValu
3534
MembersMustExist : Member 'public AutoMapper.TypeMap AutoMapper.TypeMapFactory.CreateTypeMap(System.Type, System.Type, AutoMapper.ProfileMap)' does not exist in the implementation but it does exist in the contract.
3635
CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'AutoMapper.ValueTransformerConfiguration.TransformerExpression.get()' in the contract but not the implementation.
3736
CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'AutoMapper.ValueTransformerConfiguration.ValueType.get()' in the contract but not the implementation.
38-
MembersMustExist : Member 'public void AutoMapper.Configuration.CtorParamConfigurationExpression<TSource>..ctor(System.String)' does not exist in the implementation but it does exist in the contract.
37+
TypesMustExist : Type 'AutoMapper.Configuration.CtorParamConfigurationExpression<TSource>' does not exist in the implementation but it does exist in the contract.
3938
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean AutoMapper.Configuration.ITypeMapConfiguration.IsReverseMap' is present in the implementation but not in the contract.
4039
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean AutoMapper.Configuration.ITypeMapConfiguration.IsReverseMap.get()' is present in the implementation but not in the contract.
4140
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.IgnoreAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
@@ -58,7 +57,6 @@ MembersMustExist : Member 'public System.Boolean AutoMapper.Configuration.Conven
5857
MembersMustExist : Member 'public System.Boolean AutoMapper.Configuration.Conventions.NameSplitMember.MapDestinationPropertyToSource(AutoMapper.ProfileMap, AutoMapper.TypeDetails, System.Type, System.Type, System.String, System.Collections.Generic.LinkedList<System.Reflection.MemberInfo>, AutoMapper.Configuration.Conventions.IMemberConfiguration)' does not exist in the implementation but it does exist in the contract.
5958
MembersMustExist : Member 'public System.Boolean AutoMapper.Configuration.Internal.PrimitiveHelper.IsListOrDictionaryType(System.Type)' does not exist in the implementation but it does exist in the contract.
6059
MembersMustExist : Member 'public System.Linq.Expressions.Expression AutoMapper.Execution.DelegateFactory.GenerateConstructorExpression(System.Type, AutoMapper.ProfileMap)' does not exist in the implementation but it does exist in the contract.
61-
CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.CompilerGeneratedAttribute' exists on 'AutoMapper.Internal.MemberVisitor.MemberPath.get()' in the contract but not the implementation.
6260
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean AutoMapper.QueryableExtensions.IExpressionResultConverter.CanGetExpressionResolutionResult(AutoMapper.QueryableExtensions.ExpressionResolutionResult, AutoMapper.ConstructorParameterMap)' is present in the contract but not in the implementation.
6361
MembersMustExist : Member 'public System.Boolean AutoMapper.QueryableExtensions.IExpressionResultConverter.CanGetExpressionResolutionResult(AutoMapper.QueryableExtensions.ExpressionResolutionResult, AutoMapper.ConstructorParameterMap)' does not exist in the implementation but it does exist in the contract.
6462
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean AutoMapper.QueryableExtensions.IExpressionResultConverter.CanGetExpressionResolutionResult(AutoMapper.QueryableExtensions.ExpressionResolutionResult, AutoMapper.IMemberMap)' is present in the implementation but not in the contract.
@@ -77,4 +75,4 @@ MembersMustExist : Member 'public System.Boolean AutoMapper.QueryableExtensions.
7775
MembersMustExist : Member 'public System.Boolean AutoMapper.QueryableExtensions.Impl.MemberResolverExpressionResultConverter.CanGetExpressionResolutionResult(AutoMapper.QueryableExtensions.ExpressionResolutionResult, AutoMapper.PropertyMap)' does not exist in the implementation but it does exist in the contract.
7876
MembersMustExist : Member 'public AutoMapper.QueryableExtensions.ExpressionResolutionResult AutoMapper.QueryableExtensions.Impl.MemberResolverExpressionResultConverter.GetExpressionResolutionResult(AutoMapper.QueryableExtensions.ExpressionResolutionResult, AutoMapper.ConstructorParameterMap)' does not exist in the implementation but it does exist in the contract.
7977
MembersMustExist : Member 'public AutoMapper.QueryableExtensions.ExpressionResolutionResult AutoMapper.QueryableExtensions.Impl.MemberResolverExpressionResultConverter.GetExpressionResolutionResult(AutoMapper.QueryableExtensions.ExpressionResolutionResult, AutoMapper.PropertyMap, AutoMapper.QueryableExtensions.LetPropertyMaps)' does not exist in the implementation but it does exist in the contract.
80-
Total Issues: 78
78+
Total Issues: 76

src/AutoMapper/Configuration/MappingExpressionBase.cs

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ public void Configure(TypeMap typeMap)
4343
{
4444
foreach(var destProperty in typeMap.DestinationTypeDetails.PublicWriteAccessors)
4545
{
46-
var attrs = destProperty.GetCustomAttributes(true);
47-
if(attrs.Any(x => x is IgnoreMapAttribute))
46+
if(destProperty.Has<IgnoreMapAttribute>())
4847
{
4948
IgnoreDestinationMember(destProperty);
5049
var sourceProperty = typeMap.SourceType.GetInheritedMember(destProperty.Name);
@@ -58,20 +57,7 @@ public void Configure(TypeMap typeMap)
5857
IgnoreDestinationMember(destProperty);
5958
}
6059
}
61-
62-
var destTypeInfo = typeMap.DestinationTypeDetails;
63-
if(!typeMap.DestinationType.IsAbstract)
64-
{
65-
foreach(var destCtor in destTypeInfo.Constructors.OrderByDescending(ci => ci.GetParameters().Length))
66-
{
67-
if(typeMap.Profile.MapDestinationCtorToSource(typeMap, this, destCtor, CtorParamConfigurations))
68-
{
69-
break;
70-
}
71-
}
72-
}
73-
74-
60+
MapDestinationCtorToSource(typeMap, CtorParamConfigurations);
7561
foreach (var action in TypeMapActions)
7662
{
7763
action(typeMap);
@@ -114,6 +100,49 @@ public void Configure(TypeMap typeMap)
114100
}
115101
}
116102

103+
private void MapDestinationCtorToSource(TypeMap typeMap, List<ICtorParameterConfiguration> ctorParamConfigurations)
104+
{
105+
var ctorMap = typeMap.ConstructorMap;
106+
if (ctorMap != null)
107+
{
108+
foreach (var paramMap in ctorMap.CtorParams)
109+
{
110+
paramMap.CanResolveValue = paramMap.CanResolveValue || IsConfigured(paramMap.Parameter);
111+
}
112+
return;
113+
}
114+
if (typeMap.DestinationType.IsAbstract || !typeMap.Profile.ConstructorMappingEnabled)
115+
{
116+
return;
117+
}
118+
foreach (var destCtor in typeMap.DestinationTypeDetails.Constructors.OrderByDescending(ci => ci.GetParameters().Length))
119+
{
120+
var ctorParameters = destCtor.GetParameters();
121+
if (ctorParameters.Length == 0)
122+
{
123+
break;
124+
}
125+
ctorMap = new ConstructorMap(destCtor, typeMap);
126+
foreach (var parameter in ctorParameters)
127+
{
128+
var resolvers = new LinkedList<MemberInfo>();
129+
var canResolve = typeMap.Profile.MapDestinationPropertyToSource(typeMap.SourceTypeDetails, destCtor.DeclaringType, parameter.GetType(), parameter.Name, resolvers, IsReverseMap);
130+
if ((!canResolve && parameter.IsOptional) || IsConfigured(parameter))
131+
{
132+
canResolve = true;
133+
}
134+
ctorMap.AddParameter(parameter, resolvers.ToArray(), canResolve);
135+
}
136+
typeMap.ConstructorMap = ctorMap;
137+
if (ctorMap.CanResolve)
138+
{
139+
break;
140+
}
141+
}
142+
return;
143+
bool IsConfigured(ParameterInfo parameter) => ctorParamConfigurations.Any(c => c.CtorParamName == parameter.Name);
144+
}
145+
117146
protected IEnumerable<IPropertyMapConfiguration> MapToSourceMembers() =>
118147
MemberConfigurations.Where(m => m.SourceExpression != null && m.SourceExpression.Body == m.SourceExpression.Parameters[0]);
119148

src/AutoMapper/ProfileMap.cs

Lines changed: 15 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public ProfileMap(IProfileConfiguration profile, IConfiguration configuration)
3131
EnableNullPropagationForQueryMapping = profile.EnableNullPropagationForQueryMapping ?? configuration?.EnableNullPropagationForQueryMapping ?? false;
3232
ConstructorMappingEnabled = profile.ConstructorMappingEnabled ?? configuration?.ConstructorMappingEnabled ?? true;
3333
ShouldMapField = profile.ShouldMapField ?? configuration?.ShouldMapField ?? (p => p.IsPublic());
34-
ShouldMapProperty = profile.ShouldMapProperty ?? configuration?.ShouldMapProperty ?? (p => p.IsPublic());
35-
ShouldMapMethod = profile.ShouldMapMethod ?? configuration?.ShouldMapMethod ?? (p => true);
34+
ShouldMapProperty = profile.ShouldMapProperty ?? configuration?.ShouldMapProperty ?? (p => p.IsPublic());
35+
ShouldMapMethod = profile.ShouldMapMethod ?? configuration?.ShouldMapMethod ?? (p => true);
3636
ShouldUseConstructor = profile.ShouldUseConstructor ?? configuration?.ShouldUseConstructor ?? (c => true);
3737

3838
ValueTransformers = profile.ValueTransformers.Concat(configuration?.ValueTransformers ?? Enumerable.Empty<ValueTransformerConfiguration>()).ToArray();
@@ -74,8 +74,8 @@ public ProfileMap(IProfileConfiguration profile, IConfiguration configuration)
7474
public bool EnableNullPropagationForQueryMapping { get; }
7575
public string Name { get; }
7676
public Func<FieldInfo, bool> ShouldMapField { get; }
77-
public Func<PropertyInfo, bool> ShouldMapProperty { get; }
78-
public Func<MethodInfo, bool> ShouldMapMethod { get; }
77+
public Func<PropertyInfo, bool> ShouldMapProperty { get; }
78+
public Func<MethodInfo, bool> ShouldMapMethod { get; }
7979
public Func<ConstructorInfo, bool> ShouldUseConstructor { get; }
8080

8181
public IEnumerable<Action<PropertyMap, IMemberConfigurationExpression>> AllPropertyMapActions { get; }
@@ -155,7 +155,7 @@ private void Configure(TypeMap typeMap, IConfigurationProvider configurationProv
155155
}
156156

157157
ApplyBaseMaps(typeMap, typeMap, configurationProvider);
158-
ApplyDerivedMaps(typeMap, typeMap, configurationProvider);
158+
ApplyDerivedMaps(typeMap, typeMap, configurationProvider);
159159
ApplyMemberMaps(typeMap, configurationProvider);
160160
}
161161

@@ -208,9 +208,9 @@ private void ApplyBaseMaps(TypeMap derivedMap, TypeMap currentMap, IConfiguratio
208208
}
209209

210210
private void ApplyMemberMaps(TypeMap mainMap, IConfigurationProvider configurationProvider)
211-
{
212-
AddMemberMaps(mainMap.IncludedMembers, mainMap, configurationProvider);
213-
AddMemberMaps(mainMap.GetUntypedIncludedMembers(), mainMap, configurationProvider);
211+
{
212+
AddMemberMaps(mainMap.IncludedMembers, mainMap, configurationProvider);
213+
AddMemberMaps(mainMap.GetUntypedIncludedMembers(), mainMap, configurationProvider);
214214
}
215215

216216
private void AddMemberMaps(LambdaExpression[] includedMembers, TypeMap mainMap, IConfigurationProvider configurationProvider)
@@ -225,54 +225,24 @@ private void ApplyDerivedMaps(TypeMap baseMap, TypeMap typeMap, IConfigurationPr
225225
{
226226
foreach (var derivedMap in configurationProvider.GetIncludedTypeMaps(typeMap.IncludedDerivedTypes))
227227
{
228-
derivedMap.IncludeBaseTypes(typeMap.SourceType, typeMap.DestinationType);
228+
derivedMap.IncludeBaseTypes(typeMap.SourceType, typeMap.DestinationType);
229229
derivedMap.AddInheritedMap(baseMap);
230230
ApplyDerivedMaps(baseMap, derivedMap, configurationProvider);
231231
}
232232
}
233-
234-
public bool MapDestinationCtorToSource(TypeMap typeMap,
235-
ITypeMapConfiguration typeMapConfiguration,
236-
ConstructorInfo destCtor,
237-
List<ICtorParameterConfiguration> ctorParamConfigurations)
238-
{
239-
var ctorParameters = destCtor.GetParameters();
240-
241-
if (ctorParameters.Length == 0 || !ConstructorMappingEnabled)
242-
return false;
243-
244-
var ctorMap = new ConstructorMap(destCtor, typeMap);
245-
246-
foreach (var parameter in ctorParameters)
247-
{
248-
var resolvers = new LinkedList<MemberInfo>();
249-
250-
var canResolve = MapDestinationPropertyToSource(typeMap.SourceTypeDetails, destCtor.DeclaringType, parameter.GetType(), parameter.Name, resolvers, typeMapConfiguration.IsReverseMap);
251-
if ((!canResolve && parameter.IsOptional) || ctorParamConfigurations.Any(c => c.CtorParamName == parameter.Name))
252-
{
253-
canResolve = true;
254-
}
255-
ctorMap.AddParameter(parameter, resolvers.ToArray(), canResolve);
256-
}
257-
258-
typeMap.ConstructorMap = ctorMap;
259-
260-
return ctorMap.CanResolve;
261-
}
262-
233+
263234
public bool MapDestinationPropertyToSource(TypeDetails sourceTypeInfo, Type destType, Type destMemberType, string destMemberInfo, LinkedList<MemberInfo> members, bool reverseNamingConventions)
264235
{
265236
if (string.IsNullOrEmpty(destMemberInfo))
266237
{
267238
return false;
268239
}
269-
return MemberConfigurations.Any(_ => _.MapDestinationPropertyToSource(this, sourceTypeInfo, destType, destMemberType, destMemberInfo, members, reverseNamingConventions));
240+
return MemberConfigurations.Any(memberConfig => memberConfig.MapDestinationPropertyToSource(this, sourceTypeInfo, destType, destMemberType, destMemberInfo, members, reverseNamingConventions));
270241
}
271-
272-
}
273-
242+
}
243+
274244
public readonly struct IncludedMember
275-
{
245+
{
276246
public IncludedMember(TypeMap typeMap, LambdaExpression memberExpression)
277247
{
278248
TypeMap = typeMap;
@@ -281,4 +251,4 @@ public IncludedMember(TypeMap typeMap, LambdaExpression memberExpression)
281251
public TypeMap TypeMap { get; }
282252
public LambdaExpression MemberExpression { get; }
283253
}
284-
}
254+
}

src/UnitTests/ForAllMaps.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,32 @@ public void Should_configure_all_maps()
6868
_destination2.Number.ShouldBe(-1);
6969
}
7070
}
71+
public class ForAllMapsWithConstructors : AutoMapperSpecBase
72+
{
73+
class Source
74+
{
75+
}
76+
class Destination
77+
{
78+
public Destination(int first, int second)
79+
{
80+
First = first;
81+
Second = second;
82+
}
83+
public int First { get; }
84+
public int Second { get; }
85+
}
86+
protected override MapperConfiguration Configuration => new MapperConfiguration(cfg=>
87+
{
88+
cfg.ForAllMaps((_, c) => c.ForCtorParam("second", o => o.MapFrom(s => 2)));
89+
cfg.CreateMap<Source, Destination>().ForCtorParam("first", o => o.MapFrom(s => 1));
90+
});
91+
[Fact]
92+
public void Should_map_ok()
93+
{
94+
var result = Map<Destination>(new Source());
95+
result.First.ShouldBe(1);
96+
result.Second.ShouldBe(2);
97+
}
98+
}
7199
}

0 commit comments

Comments
 (0)