Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,10 @@ public void Configure(TypeMap typeMap)
action(parameter);
}
}

public bool CheckCtorParamName(string paramName)
{
return _ctorParamName == paramName;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
public interface ICtorParameterConfiguration
{
void Configure(TypeMap typeMap);
bool CheckCtorParamName(string paramName);
}
}
1 change: 1 addition & 0 deletions src/AutoMapper/Configuration/ITypeMapConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;

namespace AutoMapper.Configuration
{
Expand Down
52 changes: 50 additions & 2 deletions src/AutoMapper/Configuration/MappingExpressionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected MappingExpressionBase(MemberList memberList, TypePair types)

public void Configure(TypeMap typeMap)
{
foreach(var destProperty in typeMap.DestinationTypeDetails.PublicWriteAccessors)
foreach (var destProperty in typeMap.DestinationTypeDetails.PublicWriteAccessors)
{
var attrs = destProperty.GetCustomAttributes(true);
if(attrs.Any(x => x is IgnoreMapAttribute))
Expand All @@ -58,7 +58,20 @@ public void Configure(TypeMap typeMap)
}
}

foreach(var action in TypeMapActions)
var destTypeInfo = typeMap.DestinationTypeDetails;
if (!typeMap.DestinationType.IsAbstract())
{
foreach (var destCtor in destTypeInfo.Constructors.OrderByDescending(ci => ci.GetParameters().Length))
{
if (MapDestinationCtorToSource(typeMap, destCtor, typeMap.SourceTypeDetails, typeMap.Profile))
{
break;
}
}
}


foreach (var action in TypeMapActions)
{
action(typeMap);
}
Expand Down Expand Up @@ -100,6 +113,41 @@ public void Configure(TypeMap typeMap)
}
}

private bool MapDestinationCtorToSource(TypeMap typeMap, ConstructorInfo destCtor, TypeDetails sourceTypeInfo, ProfileMap options)
{
var ctorParameters = destCtor.GetParameters();

if (ctorParameters.Length == 0 || !options.ConstructorMappingEnabled)
return false;

var ctorMap = new ConstructorMap(destCtor, typeMap);

foreach (var parameter in ctorParameters)
{
var resolvers = new LinkedList<MemberInfo>();

var canResolve = MapDestinationPropertyToSource(options, sourceTypeInfo, destCtor.DeclaringType, parameter.GetType(), parameter.Name, resolvers);
if ((!canResolve && parameter.IsOptional) || CtorParamConfigurations.Any(c => c.CheckCtorParamName(parameter.Name)))
{
canResolve = true;
}
ctorMap.AddParameter(parameter, resolvers.ToArray(), canResolve);
}

typeMap.ConstructorMap = ctorMap;

return ctorMap.CanResolve;
}

private bool MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceTypeInfo, Type destType, Type destMemberType, string destMemberInfo, LinkedList<MemberInfo> members)
{
if (string.IsNullOrEmpty(destMemberInfo))
{
return false;
}
return options.MemberConfigurations.Any(_ => _.MapDestinationPropertyToSource(options, sourceTypeInfo, destType, destMemberType, destMemberInfo, members));
}

protected IEnumerable<IPropertyMapConfiguration> MapToSourceMembers() =>
MemberConfigurations.Where(m => m.SourceExpression != null && m.SourceExpression.Body == m.SourceExpression.Parameters[0]);

Expand Down
1 change: 1 addition & 0 deletions src/AutoMapper/ConstructorMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq.Expressions;
using System.Reflection;
using AutoMapper.Execution;
using AutoMapper.Configuration;
using AutoMapper.QueryableExtensions;
using AutoMapper.QueryableExtensions.Impl;

Expand Down
25 changes: 13 additions & 12 deletions src/AutoMapper/ProfileMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,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 @@ -75,10 +75,10 @@ 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; }
public IEnumerable<Action<TypeMap, IMappingExpression>> AllTypeMapActions { get; }
public IEnumerable<string> GlobalIgnores { get; }
Expand Down Expand Up @@ -156,7 +156,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 @@ -209,9 +209,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 @@ -226,15 +226,16 @@ 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 readonly struct IncludedMember
{
{
public IncludedMember(TypeMap typeMap, LambdaExpression memberExpression)
{
TypeMap = typeMap;
Expand Down
60 changes: 30 additions & 30 deletions src/AutoMapper/TypeMapFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,52 +24,52 @@ public TypeMap CreateTypeMap(Type sourceType, Type destinationType, ProfileMap o
typeMap.AddPropertyMap(destProperty, resolvers);
}
}
if (!destinationType.IsAbstract())
{
foreach (var destCtor in destTypeInfo.Constructors.OrderByDescending(ci => ci.GetParameters().Length))
{
if (MapDestinationCtorToSource(typeMap, destCtor, sourceTypeInfo, options))
{
break;
}
}
}
//if (!destinationType.IsAbstract())
//{
// foreach (var destCtor in destTypeInfo.Constructors.OrderByDescending(ci => ci.GetParameters().Length))
// {
// if (MapDestinationCtorToSource(typeMap, destCtor, sourceTypeInfo, options))
// {
// break;
// }
// }
//}
return typeMap;
}

private bool MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceTypeInfo, Type destType, Type destMemberType, string destMemberInfo, LinkedList<MemberInfo> members)
{
if(string.IsNullOrEmpty(destMemberInfo))
if (string.IsNullOrEmpty(destMemberInfo))
{
return false;
}
return options.MemberConfigurations.Any(_ => _.MapDestinationPropertyToSource(options, sourceTypeInfo, destType, destMemberType, destMemberInfo, members));
}

private bool MapDestinationCtorToSource(TypeMap typeMap, ConstructorInfo destCtor, TypeDetails sourceTypeInfo, ProfileMap options)
{
var ctorParameters = destCtor.GetParameters();
//private bool MapDestinationCtorToSource(TypeMap typeMap, ConstructorInfo destCtor, TypeDetails sourceTypeInfo, ProfileMap options)
//{
// var ctorParameters = destCtor.GetParameters();

if (ctorParameters.Length == 0 || !options.ConstructorMappingEnabled)
return false;
// if (ctorParameters.Length == 0 || !options.ConstructorMappingEnabled)
// return false;

var ctorMap = new ConstructorMap(destCtor, typeMap);
// var ctorMap = new ConstructorMap(destCtor, typeMap, options);

foreach (var parameter in ctorParameters)
{
var resolvers = new LinkedList<MemberInfo>();
// foreach (var parameter in ctorParameters)
// {
// var resolvers = new LinkedList<MemberInfo>();

var canResolve = MapDestinationPropertyToSource(options, sourceTypeInfo, destCtor.DeclaringType, parameter.GetType(), parameter.Name, resolvers);
if(!canResolve && parameter.IsOptional)
{
canResolve = true;
}
ctorMap.AddParameter(parameter, resolvers.ToArray(), canResolve);
}
// var canResolve = MapDestinationPropertyToSource(options, sourceTypeInfo, destCtor.DeclaringType, parameter.GetType(), parameter.Name, resolvers);
// if((!canResolve && parameter.IsOptional) || ctorMap.ContainsCtorParameterConfig(parameter.Name))
// {
// canResolve = true;
// }
// ctorMap.AddParameter(parameter, resolvers.ToArray(), canResolve);
// }

typeMap.ConstructorMap = ctorMap;
// typeMap.ConstructorMap = ctorMap;

return ctorMap.CanResolve;
}
// return ctorMap.CanResolve;
//}
}
}
129 changes: 129 additions & 0 deletions src/UnitTests/Constructors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1609,4 +1609,133 @@ public void Should_redirect_value()
dest.Value1.ShouldBeNull();
}
}

public class When_configuring_ctor_param_members_without_source_property_1 : AutoMapperSpecBase
{
public class Source
{
public string Result { get; }

public Source(string result)
{
Result = result;
}
}

public class Dest
{
public string Result{ get; }
public dynamic Details { get; }

public Dest(string result, DestInner1 inner1)
{
Result = result;
Details = inner1;
}
public Dest(string result, DestInner2 inner2)
{
Result = result;
Details = inner2;
}

public class DestInner1
{
public int Value { get; }

public DestInner1(int value)
{
Value = value;
}
}

public class DestInner2
{
public int Value { get; }

public DestInner2(int value)
{
Value = value;
}
}
}

protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(config =>
{
config.CreateMap<Source, Dest>()
.ForCtorParam("inner1", cfg => cfg.MapFrom(_ => new Dest.DestInner1(100)));
});

[Fact]
public void Should_redirect_value()
{
var dest = Mapper.Map<Dest>(new Source("Success"));

dest.ShouldNotBeNull();
}
}

public class When_configuring_ctor_param_members_without_source_property_2 : AutoMapperSpecBase
{
public class Source
{
public string Result { get; }

public Source(string result)
{
Result = result;
}
}

public class Dest
{
public string Result{ get; }
public dynamic Details { get; }

public Dest(string result, DestInner1 inner1)
{
Result = result;
Details = inner1;
}
public Dest(string result, DestInner2 inner2)
{
Result = result;
Details = inner2;
}

public class DestInner1
{
public int Value { get; }

public DestInner1(int value)
{
Value = value;
}
}

public class DestInner2
{
public int Value { get; }

public DestInner2(int value)
{
Value = value;
}
}
}

protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(config =>
{
config.CreateMap<Source, Dest>()
.ForCtorParam("inner2", cfg => cfg.MapFrom(_ => new Dest.DestInner2(100)));
});

[Fact]
public void Should_redirect_value()
{
var dest = Mapper.Map<Dest>(new Source("Success"));

dest.ShouldNotBeNull();
}
}

}