From 5525fe54cc2664c2ae3ab3020073056b42c1f16f Mon Sep 17 00:00:00 2001 From: Lucian Bargaoanu Date: Tue, 12 May 2020 07:05:05 +0300 Subject: [PATCH 1/3] enable ForAllMaps to work with ForCtorParam --- .../Configuration/MappingExpressionBase.cs | 61 ++++++++++++++----- src/AutoMapper/ProfileMap.cs | 60 +++++------------- src/UnitTests/ForAllMaps.cs | 28 +++++++++ 3 files changed, 88 insertions(+), 61 deletions(-) diff --git a/src/AutoMapper/Configuration/MappingExpressionBase.cs b/src/AutoMapper/Configuration/MappingExpressionBase.cs index b4199303c5..c8214c04fd 100644 --- a/src/AutoMapper/Configuration/MappingExpressionBase.cs +++ b/src/AutoMapper/Configuration/MappingExpressionBase.cs @@ -43,8 +43,7 @@ public void Configure(TypeMap typeMap) { foreach(var destProperty in typeMap.DestinationTypeDetails.PublicWriteAccessors) { - var attrs = destProperty.GetCustomAttributes(true); - if(attrs.Any(x => x is IgnoreMapAttribute)) + if(destProperty.Has()) { IgnoreDestinationMember(destProperty); var sourceProperty = typeMap.SourceType.GetInheritedMember(destProperty.Name); @@ -58,20 +57,7 @@ public void Configure(TypeMap typeMap) IgnoreDestinationMember(destProperty); } } - - var destTypeInfo = typeMap.DestinationTypeDetails; - if(!typeMap.DestinationType.IsAbstract) - { - foreach(var destCtor in destTypeInfo.Constructors.OrderByDescending(ci => ci.GetParameters().Length)) - { - if(typeMap.Profile.MapDestinationCtorToSource(typeMap, this, destCtor, CtorParamConfigurations)) - { - break; - } - } - } - - + MapDestinationCtorToSource(typeMap, CtorParamConfigurations); foreach (var action in TypeMapActions) { action(typeMap); @@ -114,6 +100,49 @@ public void Configure(TypeMap typeMap) } } + private void MapDestinationCtorToSource(TypeMap typeMap, List ctorParamConfigurations) + { + var ctorMap = typeMap.ConstructorMap; + if (ctorMap != null) + { + foreach (var paramMap in ctorMap.CtorParams) + { + paramMap.CanResolveValue = paramMap.CanResolveValue || IsConfigured(paramMap.Parameter); + } + return; + } + if (typeMap.DestinationType.IsAbstract || !typeMap.Profile.ConstructorMappingEnabled) + { + return; + } + foreach (var destCtor in typeMap.DestinationTypeDetails.Constructors.OrderByDescending(ci => ci.GetParameters().Length)) + { + var ctorParameters = destCtor.GetParameters(); + if (ctorParameters.Length == 0) + { + break; + } + ctorMap = new ConstructorMap(destCtor, typeMap); + foreach (var parameter in ctorParameters) + { + var resolvers = new LinkedList(); + var canResolve = typeMap.Profile.MapDestinationPropertyToSource(typeMap.SourceTypeDetails, destCtor.DeclaringType, parameter.GetType(), parameter.Name, resolvers); + if ((!canResolve && parameter.IsOptional) || IsConfigured(parameter)) + { + canResolve = true; + } + ctorMap.AddParameter(parameter, resolvers.ToArray(), canResolve); + } + typeMap.ConstructorMap = ctorMap; + if (ctorMap.CanResolve) + { + break; + } + } + return; + bool IsConfigured(ParameterInfo parameter) => ctorParamConfigurations.Any(c => c.CtorParamName == parameter.Name); + } + protected IEnumerable MapToSourceMembers() => MemberConfigurations.Where(m => m.SourceExpression != null && m.SourceExpression.Body == m.SourceExpression.Parameters[0]); diff --git a/src/AutoMapper/ProfileMap.cs b/src/AutoMapper/ProfileMap.cs index 85b7ce32af..b213767979 100644 --- a/src/AutoMapper/ProfileMap.cs +++ b/src/AutoMapper/ProfileMap.cs @@ -31,8 +31,8 @@ public ProfileMap(IProfileConfiguration profile, IConfiguration configuration) EnableNullPropagationForQueryMapping = profile.EnableNullPropagationForQueryMapping ?? configuration?.EnableNullPropagationForQueryMapping ?? false; ConstructorMappingEnabled = profile.ConstructorMappingEnabled ?? configuration?.ConstructorMappingEnabled ?? true; ShouldMapField = profile.ShouldMapField ?? configuration?.ShouldMapField ?? (p => p.IsPublic()); - ShouldMapProperty = profile.ShouldMapProperty ?? configuration?.ShouldMapProperty ?? (p => p.IsPublic()); - ShouldMapMethod = profile.ShouldMapMethod ?? configuration?.ShouldMapMethod ?? (p => true); + ShouldMapProperty = profile.ShouldMapProperty ?? configuration?.ShouldMapProperty ?? (p => p.IsPublic()); + ShouldMapMethod = profile.ShouldMapMethod ?? configuration?.ShouldMapMethod ?? (p => true); ShouldUseConstructor = profile.ShouldUseConstructor ?? configuration?.ShouldUseConstructor ?? (c => true); ValueTransformers = profile.ValueTransformers.Concat(configuration?.ValueTransformers ?? Enumerable.Empty()).ToArray(); @@ -74,8 +74,8 @@ public ProfileMap(IProfileConfiguration profile, IConfiguration configuration) public bool EnableNullPropagationForQueryMapping { get; } public string Name { get; } public Func ShouldMapField { get; } - public Func ShouldMapProperty { get; } - public Func ShouldMapMethod { get; } + public Func ShouldMapProperty { get; } + public Func ShouldMapMethod { get; } public Func ShouldUseConstructor { get; } public IEnumerable> AllPropertyMapActions { get; } @@ -155,7 +155,7 @@ private void Configure(TypeMap typeMap, IConfigurationProvider configurationProv } ApplyBaseMaps(typeMap, typeMap, configurationProvider); - ApplyDerivedMaps(typeMap, typeMap, configurationProvider); + ApplyDerivedMaps(typeMap, typeMap, configurationProvider); ApplyMemberMaps(typeMap, configurationProvider); } @@ -208,9 +208,9 @@ private void ApplyBaseMaps(TypeMap derivedMap, TypeMap currentMap, IConfiguratio } private void ApplyMemberMaps(TypeMap mainMap, IConfigurationProvider configurationProvider) - { - AddMemberMaps(mainMap.IncludedMembers, mainMap, configurationProvider); - AddMemberMaps(mainMap.GetUntypedIncludedMembers(), mainMap, configurationProvider); + { + AddMemberMaps(mainMap.IncludedMembers, mainMap, configurationProvider); + AddMemberMaps(mainMap.GetUntypedIncludedMembers(), mainMap, configurationProvider); } private void AddMemberMaps(LambdaExpression[] includedMembers, TypeMap mainMap, IConfigurationProvider configurationProvider) @@ -225,54 +225,24 @@ private void ApplyDerivedMaps(TypeMap baseMap, TypeMap typeMap, IConfigurationPr { foreach (var derivedMap in configurationProvider.GetIncludedTypeMaps(typeMap.IncludedDerivedTypes)) { - derivedMap.IncludeBaseTypes(typeMap.SourceType, typeMap.DestinationType); + derivedMap.IncludeBaseTypes(typeMap.SourceType, typeMap.DestinationType); derivedMap.AddInheritedMap(baseMap); ApplyDerivedMaps(baseMap, derivedMap, configurationProvider); } } - - public bool MapDestinationCtorToSource(TypeMap typeMap, - ITypeMapConfiguration typeMapConfiguration, - ConstructorInfo destCtor, - List ctorParamConfigurations) - { - var ctorParameters = destCtor.GetParameters(); - - if (ctorParameters.Length == 0 || !ConstructorMappingEnabled) - return false; - - var ctorMap = new ConstructorMap(destCtor, typeMap); - - foreach (var parameter in ctorParameters) - { - var resolvers = new LinkedList(); - - var canResolve = MapDestinationPropertyToSource(typeMap.SourceTypeDetails, destCtor.DeclaringType, parameter.GetType(), parameter.Name, resolvers, typeMapConfiguration.IsReverseMap); - if ((!canResolve && parameter.IsOptional) || ctorParamConfigurations.Any(c => c.CtorParamName == parameter.Name)) - { - canResolve = true; - } - ctorMap.AddParameter(parameter, resolvers.ToArray(), canResolve); - } - - typeMap.ConstructorMap = ctorMap; - - return ctorMap.CanResolve; - } - + public bool MapDestinationPropertyToSource(TypeDetails sourceTypeInfo, Type destType, Type destMemberType, string destMemberInfo, LinkedList members, bool reverseNamingConventions) { if (string.IsNullOrEmpty(destMemberInfo)) { return false; } - return MemberConfigurations.Any(_ => _.MapDestinationPropertyToSource(this, sourceTypeInfo, destType, destMemberType, destMemberInfo, members, reverseNamingConventions)); + return MemberConfigurations.Any(memberConfig => memberConfig.MapDestinationPropertyToSource(this, sourceTypeInfo, destType, destMemberType, destMemberInfo, members, reverseNamingConventions)); } - - } - + } + public readonly struct IncludedMember - { + { public IncludedMember(TypeMap typeMap, LambdaExpression memberExpression) { TypeMap = typeMap; @@ -281,4 +251,4 @@ public IncludedMember(TypeMap typeMap, LambdaExpression memberExpression) public TypeMap TypeMap { get; } public LambdaExpression MemberExpression { get; } } -} +} \ No newline at end of file diff --git a/src/UnitTests/ForAllMaps.cs b/src/UnitTests/ForAllMaps.cs index e31ee43b5e..61dd8e92d8 100644 --- a/src/UnitTests/ForAllMaps.cs +++ b/src/UnitTests/ForAllMaps.cs @@ -68,4 +68,32 @@ public void Should_configure_all_maps() _destination2.Number.ShouldBe(-1); } } + public class ForAllMapsWithConstructors : AutoMapperSpecBase + { + class Source + { + } + class Destination + { + public Destination(int first, int second) + { + First = first; + Second = second; + } + public int First { get; } + public int Second { get; } + } + protected override MapperConfiguration Configuration => new MapperConfiguration(cfg=> + { + cfg.ForAllMaps((_, c) => c.ForCtorParam("second", o => o.MapFrom(s => 2))); + cfg.CreateMap().ForCtorParam("first", o => o.MapFrom(s => 1)); + }); + [Fact] + public void Should_map_ok() + { + var result = Map(new Source()); + result.First.ShouldBe(1); + result.Second.ShouldBe(2); + } + } } \ No newline at end of file From 428d7efdb7cdd41fcb977095d65ee874c0d4286b Mon Sep 17 00:00:00 2001 From: Lucian Bargaoanu Date: Tue, 12 May 2020 15:59:43 +0300 Subject: [PATCH 2/3] rebase --- src/AutoMapper/Configuration/MappingExpressionBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AutoMapper/Configuration/MappingExpressionBase.cs b/src/AutoMapper/Configuration/MappingExpressionBase.cs index c8214c04fd..fcd0b83f41 100644 --- a/src/AutoMapper/Configuration/MappingExpressionBase.cs +++ b/src/AutoMapper/Configuration/MappingExpressionBase.cs @@ -126,7 +126,7 @@ private void MapDestinationCtorToSource(TypeMap typeMap, List(); - var canResolve = typeMap.Profile.MapDestinationPropertyToSource(typeMap.SourceTypeDetails, destCtor.DeclaringType, parameter.GetType(), parameter.Name, resolvers); + var canResolve = typeMap.Profile.MapDestinationPropertyToSource(typeMap.SourceTypeDetails, destCtor.DeclaringType, parameter.GetType(), parameter.Name, resolvers, IsReverseMap); if ((!canResolve && parameter.IsOptional) || IsConfigured(parameter)) { canResolve = true; From 57d7c661a765e0185231f14f3464ea0936ae603e Mon Sep 17 00:00:00 2001 From: Lucian Bargaoanu Date: Tue, 12 May 2020 16:07:34 +0300 Subject: [PATCH 3/3] rebase --- src/AutoMapper/ApiCompatBaseline.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/AutoMapper/ApiCompatBaseline.txt b/src/AutoMapper/ApiCompatBaseline.txt index 26dc8b4996..63725db8a8 100644 --- a/src/AutoMapper/ApiCompatBaseline.txt +++ b/src/AutoMapper/ApiCompatBaseline.txt @@ -25,7 +25,6 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Object AutoMap InterfacesShouldHaveSameMembers : Interface member 'public TDestination AutoMapper.IRuntimeMapper.Map(System.Object, System.Action)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public TDestination AutoMapper.IRuntimeMapper.Map(TSource, System.Action>)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public TDestination AutoMapper.IRuntimeMapper.Map(TSource, TDestination, System.Action>)' is present in the implementation but not in the contract. -CannotChangeAttribute : Attribute 'System.Runtime.CompilerServices.IteratorStateMachineAttribute' on 'AutoMapper.MapperConfiguration.GetIncludedTypeMaps(System.Collections.Generic.IEnumerable)' changed from '[IteratorStateMachineAttribute(typeof(MapperConfiguration.d__69))]' in the contract to '[IteratorStateMachineAttribute(typeof(MapperConfiguration.d__72))]' in the implementation. TypesMustExist : Type 'AutoMapper.MemberFinderVisitor' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Boolean AutoMapper.ProfileMap.MapDestinationCtorToSource(AutoMapper.TypeMap, System.Reflection.ConstructorInfo, AutoMapper.TypeDetails, System.Collections.Generic.List)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Boolean AutoMapper.ProfileMap.MapDestinationPropertyToSource(AutoMapper.TypeDetails, System.Type, System.Type, System.String, System.Collections.Generic.LinkedList)' does not exist in the implementation but it does exist in the contract. @@ -35,7 +34,7 @@ MembersMustExist : Member 'public void AutoMapper.PropertyMap.UseDestinationValu 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. CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'AutoMapper.ValueTransformerConfiguration.TransformerExpression.get()' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.IsReadOnlyAttribute' exists on 'AutoMapper.ValueTransformerConfiguration.ValueType.get()' in the contract but not the implementation. -MembersMustExist : Member 'public void AutoMapper.Configuration.CtorParamConfigurationExpression..ctor(System.String)' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'AutoMapper.Configuration.CtorParamConfigurationExpression' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean AutoMapper.Configuration.ITypeMapConfiguration.IsReverseMap' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean AutoMapper.Configuration.ITypeMapConfiguration.IsReverseMap.get()' is present in the implementation but not in the contract. 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 MembersMustExist : Member 'public System.Boolean AutoMapper.Configuration.Conventions.NameSplitMember.MapDestinationPropertyToSource(AutoMapper.ProfileMap, AutoMapper.TypeDetails, System.Type, System.Type, System.String, System.Collections.Generic.LinkedList, AutoMapper.Configuration.Conventions.IMemberConfiguration)' does not exist in the implementation but it does exist in the contract. 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. 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. -CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.CompilerGeneratedAttribute' exists on 'AutoMapper.Internal.MemberVisitor.MemberPath.get()' in the contract but not the implementation. 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. 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. 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. 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. 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. 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. -Total Issues: 78 +Total Issues: 76