diff --git a/docs/Construction.md b/docs/Construction.md
index d2b3676998..77510c2793 100644
--- a/docs/Construction.md
+++ b/docs/Construction.md
@@ -41,8 +41,15 @@ Mapper.Initialize(cfg =>
This works for both LINQ projections and in-memory mapping.
-You can also disable constructor mapping :
+You can also disable constructor mapping:
```c#
Mapper.Initialize(cfg => cfg.DisableConstructorMapping());
```
+
+You can configure which constructors are considered for the destination object:
+
+```c#
+// don't map private constructors
+Mapper.Initialize(cfg => cfg.ShouldUseConstructor = ci => !ci.IsPrivate);
+```
diff --git a/src/AutoMapper/Configuration/IProfileConfiguration.cs b/src/AutoMapper/Configuration/IProfileConfiguration.cs
index 10ce0e66d2..6be4197de3 100644
--- a/src/AutoMapper/Configuration/IProfileConfiguration.cs
+++ b/src/AutoMapper/Configuration/IProfileConfiguration.cs
@@ -29,7 +29,7 @@ public interface IProfileConfiguration
///
/// Specify which properties should be mapped.
- /// By default only public properties are mapped.e
+ /// By default only public properties are mapped.
///
Func ShouldMapProperty { get; }
@@ -39,6 +39,12 @@ public interface IProfileConfiguration
///
Func ShouldMapField { get; }
+ ///
+ /// Specify which constructors should be considered for the destination objects.
+ /// By default all constructors are considered.
+ ///
+ Func ShouldUseConstructor { get; }
+
string ProfileName { get; }
IEnumerable GlobalIgnores { get; }
INamingConvention SourceMemberNamingConvention { get; }
@@ -47,4 +53,4 @@ public interface IProfileConfiguration
IEnumerable OpenTypeMapConfigs { get; }
IEnumerable ValueTransformers { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/AutoMapper/IProfileExpression.cs b/src/AutoMapper/IProfileExpression.cs
index 8cecf71947..a012df945c 100644
--- a/src/AutoMapper/IProfileExpression.cs
+++ b/src/AutoMapper/IProfileExpression.cs
@@ -145,6 +145,8 @@ public interface IProfileExpression
Func ShouldMapProperty { get; set; }
Func ShouldMapField { get; set; }
+ Func ShouldUseConstructor { get; set; }
+
string ProfileName { get; }
IMemberConfiguration AddMemberConfiguration();
IConditionalObjectMapper AddConditionalObjectMapper();
diff --git a/src/AutoMapper/Profile.cs b/src/AutoMapper/Profile.cs
index c7f8c63ba0..cb43a8f06f 100644
--- a/src/AutoMapper/Profile.cs
+++ b/src/AutoMapper/Profile.cs
@@ -74,6 +74,7 @@ IEnumerable> IProfileConfigu
public bool? EnableNullPropagationForQueryMapping { get; set; }
public Func ShouldMapProperty { get; set; }
public Func ShouldMapField { get; set; }
+ public Func ShouldUseConstructor { get; set; }
public INamingConvention SourceMemberNamingConvention { get; set; }
public INamingConvention DestinationMemberNamingConvention { get; set; }
diff --git a/src/AutoMapper/ProfileMap.cs b/src/AutoMapper/ProfileMap.cs
index f1e409e857..96cfe9f741 100644
--- a/src/AutoMapper/ProfileMap.cs
+++ b/src/AutoMapper/ProfileMap.cs
@@ -34,6 +34,7 @@ public ProfileMap(IProfileConfiguration profile, IConfiguration configuration)
ConstructorMappingEnabled = profile.ConstructorMappingEnabled ?? configuration?.ConstructorMappingEnabled ?? true;
ShouldMapField = profile.ShouldMapField ?? configuration?.ShouldMapField ?? (p => p.IsPublic());
ShouldMapProperty = profile.ShouldMapProperty ?? configuration?.ShouldMapProperty ?? (p => p.IsPublic());
+ ShouldUseConstructor = profile.ShouldUseConstructor ?? configuration?.ShouldUseConstructor ?? (c => true);
CreateMissingTypeMaps = profile.CreateMissingTypeMaps ?? configuration?.CreateMissingTypeMaps ?? true;
ValidateInlineMaps = profile.ValidateInlineMaps ?? configuration?.ValidateInlineMaps ?? true;
@@ -83,6 +84,7 @@ public ProfileMap(IProfileConfiguration profile, IConfiguration configuration)
public string Name { get; }
public Func ShouldMapField { get; }
public Func ShouldMapProperty { get; }
+ public Func ShouldUseConstructor { get; }
public IEnumerable> AllPropertyMapActions { get; }
public IEnumerable> AllTypeMapActions { get; }
@@ -124,9 +126,9 @@ public void Configure(IConfigurationProvider configurationProvider)
}
private void BuildTypeMap(IConfigurationProvider configurationProvider, ITypeMapConfiguration config)
- {
+ {
var typeMap = _typeMapFactory.CreateTypeMap(config.SourceType, config.DestinationType, this);
-
+
config.Configure(typeMap);
configurationProvider.RegisterTypeMap(typeMap);
@@ -164,7 +166,7 @@ private void Configure(TypeMap typeMap, IConfigurationProvider configurationProv
ApplyBaseMaps(typeMap, typeMap, configurationProvider);
ApplyDerivedMaps(typeMap, typeMap, configurationProvider);
}
-
+
public bool IsConventionMap(TypePair types) => TypeConfigurations.Any(c => c.IsMatch(types));
public TypeMap CreateConventionTypeMap(TypePair types, IConfigurationProvider configurationProvider)
@@ -177,8 +179,8 @@ public TypeMap CreateConventionTypeMap(TypePair types, IConfigurationProvider co
config.Configure(typeMap);
- Configure(typeMap, configurationProvider);
-
+ Configure(typeMap, configurationProvider);
+
return typeMap;
}
@@ -188,8 +190,8 @@ public TypeMap CreateInlineMap(TypePair types, IConfigurationProvider configurat
typeMap.IsConventionMap = true;
- Configure(typeMap, configurationProvider);
-
+ Configure(typeMap, configurationProvider);
+
return typeMap;
}
@@ -215,7 +217,7 @@ public TypeMap CreateClosedGenericTypeMap(ITypeMapConfiguration openMapConfig, T
{
var neededParameters = closedMap.DestinationTypeOverride.GetGenericParameters().Length;
closedMap.DestinationTypeOverride = closedMap.DestinationTypeOverride.MakeGenericType(closedTypes.DestinationType.GetGenericArguments().Take(neededParameters).ToArray());
- }
+ }
return closedMap;
}
@@ -244,30 +246,30 @@ private void ApplyBaseMaps(TypeMap derivedMap, TypeMap currentMap, IConfiguratio
private void ApplyDerivedMaps(TypeMap baseMap, TypeMap typeMap, IConfigurationProvider configurationProvider)
{
foreach (var inheritedTypeMap in GetIncludedTypeMaps(typeMap.IncludedDerivedTypes, configurationProvider))
- {
- inheritedTypeMap.IncludeBaseTypes(typeMap.SourceType, typeMap.DestinationType);
+ {
+ inheritedTypeMap.IncludeBaseTypes(typeMap.SourceType, typeMap.DestinationType);
inheritedTypeMap.AddInheritedMap(baseMap);
ApplyDerivedMaps(baseMap, inheritedTypeMap, configurationProvider);
}
- }
-
+ }
+
private static IEnumerable GetIncludedTypeMaps(IEnumerable includedTypes, IConfigurationProvider configurationProvider)
- {
+ {
foreach(var pair in includedTypes)
- {
- var typeMap = configurationProvider.FindTypeMapFor(pair);
+ {
+ var typeMap = configurationProvider.FindTypeMapFor(pair);
if(typeMap != null)
- {
+ {
yield return typeMap;
}
- typeMap = configurationProvider.ResolveTypeMap(pair);
- // we want the exact map the user included, but we could instantiate an open generic
+ typeMap = configurationProvider.ResolveTypeMap(pair);
+ // we want the exact map the user included, but we could instantiate an open generic
if(typeMap == null || typeMap.Types != pair || typeMap.IsConventionMap)
{
throw QueryMapperHelper.MissingMapException(pair);
- }
+ }
yield return typeMap;
- }
+ }
}
}
}
\ No newline at end of file
diff --git a/src/AutoMapper/TypeDetails.cs b/src/AutoMapper/TypeDetails.cs
index d4e88d322f..9b3bdf165a 100644
--- a/src/AutoMapper/TypeDetails.cs
+++ b/src/AutoMapper/TypeDetails.cs
@@ -23,7 +23,7 @@ public TypeDetails(Type type, ProfileMap config)
PublicReadAccessors = BuildPublicReadAccessors(publicReadableMembers);
PublicWriteAccessors = BuildPublicAccessors(publicWritableMembers);
PublicNoArgMethods = BuildPublicNoArgMethods();
- Constructors = type.GetDeclaredConstructors().Where(ci => !ci.IsStatic).ToArray();
+ Constructors = GetAllConstructors(config.ShouldUseConstructor);
PublicNoArgExtensionMethods = BuildPublicNoArgExtensionMethods(config.SourceExtensionMethods);
AllMembers = PublicReadAccessors.Concat(PublicNoArgMethods).Concat(PublicNoArgExtensionMethods).ToList();
DestinationMemberNames = AllMembers.Select(mi => new DestinationMemberName { Member = mi, Possibles = PossibleNames(mi.Name, config.Prefixes, config.Postfixes).ToArray() });
@@ -59,7 +59,9 @@ private static IEnumerable PostFixes(IEnumerable postfixes, stri
.Select(postfix => name.Remove(name.Length - postfix.Length));
}
- private static Func MembersToMap(Func shouldMapProperty, Func shouldMapField)
+ private static Func MembersToMap(
+ Func shouldMapProperty,
+ Func shouldMapField)
{
return m =>
{
@@ -70,7 +72,7 @@ private static Func MembersToMap(Func shou
case FieldInfo field:
return !field.IsStatic && shouldMapField(field);
default:
- throw new ArgumentException("Should be a field or property.");
+ throw new ArgumentException("Should be a field or a property.");
}
};
}
@@ -163,6 +165,11 @@ private IEnumerable GetAllPublicReadableMembers(Func GetAllPublicWritableMembers(Func membersToMap)
=> GetAllPublicMembers(PropertyWritable, FieldWritable, membersToMap);
+
+ private IEnumerable GetAllConstructors(Func shouldUseConstructor)
+ {
+ return Type.GetDeclaredConstructors().Where(shouldUseConstructor).ToArray();
+ }
private static bool PropertyReadable(PropertyInfo propertyInfo) => propertyInfo.CanRead;
diff --git a/src/AutoMapper/TypeMap.cs b/src/AutoMapper/TypeMap.cs
index 8ef43ffe40..3d25cd6ad2 100644
--- a/src/AutoMapper/TypeMap.cs
+++ b/src/AutoMapper/TypeMap.cs
@@ -182,10 +182,8 @@ public bool PassesCtorValidation()
if (DestinationTypeToUse.IsValueType())
return true;
- var constructors = DestinationTypeToUse
- .GetDeclaredConstructors()
- .Where(ci => !ci.IsStatic);
-
+ var constructors = DestinationTypeDetails.Constructors;
+
//find a ctor with only optional args
var ctorWithOptionalArgs = constructors.FirstOrDefault(c => c.GetParameters().All(p => p.IsOptional));
diff --git a/src/UnitTests/ShouldUseConstructor.cs b/src/UnitTests/ShouldUseConstructor.cs
new file mode 100644
index 0000000000..474e56971a
--- /dev/null
+++ b/src/UnitTests/ShouldUseConstructor.cs
@@ -0,0 +1,160 @@
+using System.Linq;
+using Shouldly;
+using Xunit;
+
+namespace AutoMapper.UnitTests
+{
+ public class ShouldUseConstructorInternal : NonValidatingSpecBase
+ {
+ class Destination
+ {
+ internal Destination(int a, string b)
+ {
+ }
+
+ public int A { get; }
+
+ public string B { get; }
+
+ public Destination(int a)
+ {
+
+ }
+
+ private Destination()
+ {
+ }
+ }
+
+ class Source
+ {
+ public int A { get; set; }
+ }
+
+ protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(
+ cfg =>
+ {
+ cfg.ShouldUseConstructor = c => c.IsAssembly;
+ cfg.CreateMap();
+ });
+
+ [Fact]
+ public void Should_only_map_internal_ctor()
+ {
+ Should.Throw(() =>
+ Configuration.AssertConfigurationIsValid());
+ }
+ }
+
+ public class ShouldUseConstructorPrivate : NonValidatingSpecBase
+ {
+
+ class Destination
+ {
+ private Destination(int a, string b)
+ {
+ }
+
+ public int A { get; }
+
+ public string B { get; }
+
+ internal Destination(int a)
+ {
+
+ }
+
+ public Destination()
+ {
+ }
+ }
+
+ class Source
+ {
+ public int A { get; set; }
+ }
+
+ protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(
+ cfg =>
+ {
+ cfg.ShouldUseConstructor = c => c.IsPrivate;
+ cfg.CreateMap();
+ });
+
+ [Fact]
+ public void Should_only_map_private_ctor()
+ {
+ Should.Throw(() =>
+ Configuration.AssertConfigurationIsValid());
+ }
+ }
+
+ public class ShouldUseConstructorPublic : NonValidatingSpecBase
+ {
+ class Destination
+ {
+ public Destination(int a, string b)
+ {
+ }
+
+ public int A { get; }
+
+ public string B { get; }
+
+ internal Destination(int a)
+ {
+
+ }
+
+ private Destination()
+ {
+ }
+ }
+
+ class Source
+ {
+ public int A { get; set; }
+ }
+
+ protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(
+ cfg =>
+ {
+ cfg.ShouldUseConstructor = c => c.IsPublic;
+ cfg.CreateMap();
+ });
+
+ [Fact]
+ public void Should_only_map_public_ctor()
+ {
+ Should.Throw(() =>
+ Configuration.AssertConfigurationIsValid());
+ }
+ }
+
+
+ public class ShouldUseConstructorDefault : AutoMapperSpecBase
+ {
+ class Destination
+ {
+ public Destination(int a, string b)
+ {
+ }
+
+ public int A { get; }
+
+ public string B { get; }
+
+ private Destination()
+ {
+ }
+ }
+
+ class Source
+ {
+ public int A { get; set; }
+ }
+
+ protected override MapperConfiguration Configuration { get; } =
+ new MapperConfiguration(cfg => { cfg.CreateMap(); });
+ }
+}