Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/AutoMapper/ApiCompatBaseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Object AutoMap
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.
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.
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.
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.
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<AutoMapper.Configuration.ICtorParameterConfiguration>)' 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<System.Reflection.MemberInfo>)' does not exist in the implementation but it does exist in the contract.
Expand All @@ -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<TSource>..ctor(System.String)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'AutoMapper.Configuration.CtorParamConfigurationExpression<TSource>' 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.
Expand All @@ -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<System.Reflection.MemberInfo>, 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.
Expand All @@ -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
61 changes: 45 additions & 16 deletions src/AutoMapper/Configuration/MappingExpressionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IgnoreMapAttribute>())
{
IgnoreDestinationMember(destProperty);
var sourceProperty = typeMap.SourceType.GetInheritedMember(destProperty.Name);
Expand All @@ -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);
Expand Down Expand Up @@ -114,6 +100,49 @@ public void Configure(TypeMap typeMap)
}
}

private void MapDestinationCtorToSource(TypeMap typeMap, List<ICtorParameterConfiguration> 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<MemberInfo>();
var canResolve = typeMap.Profile.MapDestinationPropertyToSource(typeMap.SourceTypeDetails, destCtor.DeclaringType, parameter.GetType(), parameter.Name, resolvers, IsReverseMap);
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<IPropertyMapConfiguration> MapToSourceMembers() =>
MemberConfigurations.Where(m => m.SourceExpression != null && m.SourceExpression.Body == m.SourceExpression.Parameters[0]);

Expand Down
60 changes: 15 additions & 45 deletions src/AutoMapper/ProfileMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ValueTransformerConfiguration>()).ToArray();
Expand Down Expand Up @@ -74,8 +74,8 @@ public ProfileMap(IProfileConfiguration profile, IConfiguration configuration)
public bool EnableNullPropagationForQueryMapping { get; }
public string Name { get; }
public Func<FieldInfo, bool> ShouldMapField { get; }
public Func<PropertyInfo, bool> ShouldMapProperty { get; }
public Func<MethodInfo, bool> ShouldMapMethod { get; }
public Func<PropertyInfo, bool> ShouldMapProperty { get; }
public Func<MethodInfo, bool> ShouldMapMethod { get; }
public Func<ConstructorInfo, bool> ShouldUseConstructor { get; }

public IEnumerable<Action<PropertyMap, IMemberConfigurationExpression>> AllPropertyMapActions { get; }
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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)
Expand All @@ -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<ICtorParameterConfiguration> 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<MemberInfo>();

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<MemberInfo> 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;
Expand All @@ -281,4 +251,4 @@ public IncludedMember(TypeMap typeMap, LambdaExpression memberExpression)
public TypeMap TypeMap { get; }
public LambdaExpression MemberExpression { get; }
}
}
}
28 changes: 28 additions & 0 deletions src/UnitTests/ForAllMaps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Source, Destination>().ForCtorParam("first", o => o.MapFrom(s => 1));
});
[Fact]
public void Should_map_ok()
{
var result = Map<Destination>(new Source());
result.First.ShouldBe(1);
result.Second.ShouldBe(2);
}
}
}