diff --git a/.editorconfig b/.editorconfig
index 8b620e3e4c..74089b2787 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -2,7 +2,7 @@
root = true
[*]
-end_of_line = LF
+end_of_line = lf
indent_style = space
indent_size = 4
@@ -21,7 +21,7 @@ dotnet_naming_style.constant_style.capitalization = pascal_case
# Static fields are PascalCase
dotnet_naming_rule.static_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.static_fields_should_be_pascal_case.symbols = static_fields
-dotnet_naming_rule.static_fields_should_be_pascal_case.style = static_field_style
+dotnet_naming_rule.static_fields_should_be_pascal_case.style = constant_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.applicable_accessibilities = *
@@ -32,7 +32,7 @@ dotnet_naming_style.static_field_style.capitalization = pascal_case
# Public and internal fields are PascalCase
dotnet_naming_rule.public_and_internal_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.public_and_internal_should_be_pascal_case.symbols = public_internal_fields
-dotnet_naming_rule.public_and_internal_should_be_pascal_case.style = public_internal_fields_style
+dotnet_naming_rule.public_and_internal_should_be_pascal_case.style = constant_style
dotnet_naming_symbols.public_internal_fields.applicable_kinds = field
dotnet_naming_symbols.public_internal_fields.applicable_accessibilities = public, internal
@@ -58,6 +58,32 @@ dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
dotnet_naming_style.camel_case_style.capitalization = camel_case
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+tab_width = 4
+csharp_indent_labels = one_less_than_current
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_auto_properties = true:silent
+csharp_using_directive_placement = outside_namespace:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_prefer_braces = true:silent
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_top_level_statements = true:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+dotnet_style_object_initializer = true:suggestion
+csharp_style_namespace_declarations = file_scoped:suggestion
+dotnet_style_namespace_match_folder = true:silent
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
[*.xml]
indent_size = 2
diff --git a/Directory.Build.props b/Directory.Build.props
index a585cc8acf..52fa6c08a1 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -6,6 +6,15 @@
$(NoWarn);CS1701;CS1702;CS1591truestrict
+ enable
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index 13a595f50b..9b288ebad0 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,9 @@ var configuration = new MapperConfiguration(cfg =>
cfg.CreateMap();
});
// only during development, validate your mappings; remove it before release
+#if DEBUG
configuration.AssertConfigurationIsValid();
+#endif
// use DI (http://docs.automapper.org/en/latest/Dependency-injection.html) or create the mapper yourself
var mapper = configuration.CreateMapper();
```
diff --git a/docs/12.0-Upgrade-Guide.md b/docs/12.0-Upgrade-Guide.md
index b454ada3f8..580e213d93 100644
--- a/docs/12.0-Upgrade-Guide.md
+++ b/docs/12.0-Upgrade-Guide.md
@@ -17,4 +17,9 @@ Another possible occurence is with `ForAllMaps` and `ForAllPropertyMaps` when it
You should use `ResolutionContext.Items` to access the items passed in the `Map` call.
-Instead of `ServiceCtor` you should use dependency injection or pass the needed objects in the `Map` call.
\ No newline at end of file
+Instead of `ServiceCtor`, you should use dependency injection or pass the needed objects in the `Map` call.
+
+## Naming conventions
+
+We've simplified the implementation for performance reasons. If that doesn't work for you, you can write your own naming convention. Rather than address every
+peculiarity, we prefer to have a simple and fast implementation that covers most cases.
\ No newline at end of file
diff --git a/docs/Configuration.md b/docs/Configuration.md
index faf54664a4..318890ec64 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -93,23 +93,23 @@ You can set the source and destination naming conventions
```c#
var configuration = new MapperConfiguration(cfg => {
- cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
- cfg.DestinationMemberNamingConvention = new PascalCaseNamingConvention();
+ cfg.SourceMemberNamingConvention = LowerUnderscoreNamingConvention.Instance;
+ cfg.DestinationMemberNamingConvention = PascalCaseNamingConvention.Instance;
});
```
This will map the following properties to each other:
` property_name -> PropertyName `
-You can also set this at a per profile level
+You can also set this per profile
```c#
public class OrganizationProfile : Profile
{
public OrganizationProfile()
{
- SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
- DestinationMemberNamingConvention = new PascalCaseNamingConvention();
+ SourceMemberNamingConvention = LowerUnderscoreNamingConvention.Instance;
+ DestinationMemberNamingConvention = PascalCaseNamingConvention.Instance;
//Put your CreateMap... Etc.. here
}
}
diff --git a/docs/Construction.md b/docs/Construction.md
index 282e109f3d..40286cdb16 100644
--- a/docs/Construction.md
+++ b/docs/Construction.md
@@ -50,6 +50,6 @@ var configuration = new MapperConfiguration(cfg => cfg.DisableConstructorMapping
You can configure which constructors are considered for the destination object:
```c#
-// don't map private constructors
-var configuration = new MapperConfiguration(cfg => cfg.ShouldUseConstructor = ci => !ci.IsPrivate);
-```
+// use only public constructors
+var configuration = new MapperConfiguration(cfg => cfg.ShouldUseConstructor = constructor => constructor.IsPublic);
+```
\ No newline at end of file
diff --git a/src/AutoMapper/ApiCompatBaseline.txt b/src/AutoMapper/ApiCompatBaseline.txt
index 203ce3e707..b705fb766c 100644
--- a/src/AutoMapper/ApiCompatBaseline.txt
+++ b/src/AutoMapper/ApiCompatBaseline.txt
@@ -1,11 +1,60 @@
Compat issues with assembly AutoMapper:
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.AutoMapAttribute' changed from '[AttributeUsageAttribute(1036, AllowMultiple=true)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple=true)]' in the implementation.
+MembersMustExist : Member 'public System.Object System.Object AutoMapper.ContextCacheKey.Source' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.ContextCacheKey.Equals(AutoMapper.ContextCacheKey)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.ContextCacheKey.op_Equality(AutoMapper.ContextCacheKey, AutoMapper.ContextCacheKey)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.ContextCacheKey.op_Inequality(AutoMapper.ContextCacheKey, AutoMapper.ContextCacheKey)' does not exist in the implementation but it does exist in the contract.
+CannotSealType : Type 'AutoMapper.ExactMatchNamingConvention' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
+MembersMustExist : Member 'public System.String AutoMapper.ExactMatchNamingConvention.ReplaceValue(System.Text.RegularExpressions.Match)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Text.RegularExpressions.Regex AutoMapper.ExactMatchNamingConvention.SplittingExpression.get()' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public TMappingExpression AutoMapper.IMappingExpressionBase.AsProxy()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.IMappingExpressionBase.AsProxy()' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void AutoMapper.IMappingExpressionBase.AsProxy()' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.Dictionary AutoMapper.IMappingOperationOptions.Items.get()' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IDictionary AutoMapper.IMappingOperationOptions.Items.get()' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public System.Collections.Generic.IDictionary AutoMapper.IMappingOperationOptions.Items.get()' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Text.RegularExpressions.Regex AutoMapper.INamingConvention.SplittingExpression' is present in the contract but not in the implementation.
+InterfacesShouldHaveSameMembers : Interface member 'public System.String AutoMapper.INamingConvention.ReplaceValue(System.Text.RegularExpressions.Match)' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public System.String AutoMapper.INamingConvention.ReplaceValue(System.Text.RegularExpressions.Match)' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.String[] AutoMapper.INamingConvention.Split(System.String)' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Text.RegularExpressions.Regex AutoMapper.INamingConvention.SplittingExpression.get()' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public System.Text.RegularExpressions.Regex AutoMapper.INamingConvention.SplittingExpression.get()' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection AutoMapper.IProfileConfiguration.MemberConfigurations' is present in the contract but not in the implementation.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection AutoMapper.IProfileConfiguration.MemberConfigurations.get()' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyCollection AutoMapper.IProfileConfiguration.MemberConfigurations.get()' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection AutoMapper.IProfileConfiguration.OpenTypeMapConfigs.get()' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection AutoMapper.IProfileConfiguration.OpenTypeMapConfigs.get()' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyCollection AutoMapper.IProfileConfiguration.OpenTypeMapConfigs.get()' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection AutoMapper.IProfileConfiguration.TypeMapConfigs.get()' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection AutoMapper.IProfileConfiguration.TypeMapConfigs.get()' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyCollection AutoMapper.IProfileConfiguration.TypeMapConfigs.get()' does not exist in the implementation but it does exist in the contract.
+CannotSealType : Type 'AutoMapper.LowerUnderscoreNamingConvention' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
+MembersMustExist : Member 'public System.String AutoMapper.LowerUnderscoreNamingConvention.ReplaceValue(System.Text.RegularExpressions.Match)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Text.RegularExpressions.Regex AutoMapper.LowerUnderscoreNamingConvention.SplittingExpression.get()' does not exist in the implementation but it does exist in the contract.
+CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.CompilerGeneratedAttribute' exists on 'AutoMapper.MapperConfigurationExpression.AutoMapper.Internal.IGlobalConfigurationExpression.get_ProjectionMappers()' in the contract but not the implementation.
+MembersMustExist : Member 'public System.Collections.Generic.IDictionary AutoMapper.MappingOperationOptions.Items.get()' does not exist in the implementation but it does exist in the contract.
+CannotSealType : Type 'AutoMapper.PascalCaseNamingConvention' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
+MembersMustExist : Member 'public System.String AutoMapper.PascalCaseNamingConvention.ReplaceValue(System.Text.RegularExpressions.Match)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Text.RegularExpressions.Regex AutoMapper.PascalCaseNamingConvention.SplittingExpression.get()' does not exist in the implementation but it does exist in the contract.
+CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.CompilerGeneratedAttribute' exists on 'AutoMapper.Profile.DestinationMemberNamingConvention.get()' in the contract but not the implementation.
+CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.CompilerGeneratedAttribute' exists on 'AutoMapper.Profile.DestinationMemberNamingConvention.set(AutoMapper.INamingConvention)' in the contract but not the implementation.
+CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.CompilerGeneratedAttribute' exists on 'AutoMapper.Profile.SourceMemberNamingConvention.get()' in the contract but not the implementation.
+CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.CompilerGeneratedAttribute' exists on 'AutoMapper.Profile.SourceMemberNamingConvention.set(AutoMapper.INamingConvention)' in the contract but not the implementation.
+MembersMustExist : Member 'public System.Collections.Generic.IDictionary AutoMapper.ResolutionContext.Items.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public AutoMapper.IMappingOperationOptions AutoMapper.ResolutionContext.Options.get()' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'AutoMapper.ValueResolverConfiguration' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Linq.Expressions.LambdaExpression System.Linq.Expressions.LambdaExpression AutoMapper.ValueTransformerConfiguration.TransformerExpression' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Type System.Type AutoMapper.ValueTransformerConfiguration.ValueType' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Default interface member 'public System.Boolean AutoMapper.Configuration.IPropertyMapConfiguration.Ignored' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Default interface member 'public System.Boolean AutoMapper.Configuration.IPropertyMapConfiguration.Ignored.get()' is present in the implementation but not in the contract.
+CannotRemoveBaseTypeOrInterface : Type 'AutoMapper.Configuration.MappingExpression' does not inherit from base type 'AutoMapper.Configuration.MappingExpressionBase' in the implementation but it does in the contract.
+TypesMustExist : Type 'AutoMapper.Configuration.MappingExpression.MemberConfigurationExpression' does not exist in the implementation but it does exist in the contract.
+CannotRemoveBaseTypeOrInterface : Type 'AutoMapper.Configuration.MappingExpression' does not inherit from base type 'AutoMapper.Configuration.MappingExpressionBase' in the implementation but it does in the contract.
+TypesMustExist : Type 'AutoMapper.Configuration.MappingExpressionBase' does not exist in the implementation but it does exist in the contract.
+CannotRemoveBaseTypeOrInterface : Type 'AutoMapper.Configuration.MappingExpressionBase' does not inherit from base type 'AutoMapper.Configuration.MappingExpressionBase' in the implementation but it does in the contract.
MembersMustExist : Member 'public void AutoMapper.Configuration.MappingExpressionBase.AsProxy()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void AutoMapper.Configuration.ValidationContext..ctor(AutoMapper.Internal.TypePair, AutoMapper.MemberMap, AutoMapper.Internal.Mappers.IObjectMapper)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void AutoMapper.Configuration.ValidationContext..ctor(AutoMapper.Internal.TypePair, AutoMapper.MemberMap, AutoMapper.TypeMap)' does not exist in the implementation but it does exist 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.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.MapAtRuntimeAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.MappingOrderAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
@@ -14,7 +63,63 @@ CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMappe
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.UseExistingValueAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueConverterAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueResolverAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
+TypesMustExist : Type 'AutoMapper.Configuration.Conventions.DefaultName' does not exist in the implementation but it does exist in the contract.
+TypesMustExist : Type 'AutoMapper.Configuration.Conventions.IParentSourceToDestinationNameMapper' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Reflection.MemberInfo AutoMapper.Configuration.Conventions.ISourceToDestinationNameMapper.GetMatchingMemberInfo(AutoMapper.Internal.TypeDetails, System.Type, System.Type, System.String)' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public System.Reflection.MemberInfo AutoMapper.Configuration.Conventions.ISourceToDestinationNameMapper.GetMatchingMemberInfo(AutoMapper.Internal.TypeDetails, System.Type, System.Type, System.String)' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Reflection.MemberInfo AutoMapper.Configuration.Conventions.ISourceToDestinationNameMapper.GetSourceMember(AutoMapper.Internal.TypeDetails, System.Type, System.Type, System.String)' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.Configuration.Conventions.ISourceToDestinationNameMapper.Merge(AutoMapper.Configuration.Conventions.ISourceToDestinationNameMapper)' is present in the implementation but not in the contract.
+TypesMustExist : Type 'AutoMapper.Configuration.Conventions.ParentSourceToDestinationNameMapper' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Reflection.MemberInfo AutoMapper.Configuration.Conventions.PrePostfixName.GetMatchingMemberInfo(AutoMapper.Internal.TypeDetails, System.Type, System.Type, System.String)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Collections.Generic.List AutoMapper.Configuration.Conventions.PrePostfixName.Postfixes.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Collections.Generic.List AutoMapper.Configuration.Conventions.PrePostfixName.Prefixes.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public AutoMapper.Configuration.Conventions.ReplaceName AutoMapper.Configuration.Conventions.ReplaceName.AddReplace(System.String, System.String)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Reflection.MemberInfo AutoMapper.Configuration.Conventions.ReplaceName.GetMatchingMemberInfo(AutoMapper.Internal.TypeDetails, System.Type, System.Type, System.String)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Linq.Expressions.Expression System.Linq.Expressions.Expression AutoMapper.Execution.Member.Expression' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Linq.Expressions.Expression System.Linq.Expressions.Expression AutoMapper.Execution.Member.Target' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Reflection.MemberInfo System.Reflection.MemberInfo AutoMapper.Execution.Member.MemberInfo' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean System.Boolean AutoMapper.Execution.PropertyDescription.CanWrite' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.String System.String AutoMapper.Execution.PropertyDescription.Name' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Type System.Type AutoMapper.Execution.PropertyDescription.Type' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.Execution.PropertyDescription.Equals(AutoMapper.Execution.PropertyDescription)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.Execution.PropertyDescription.op_Equality(AutoMapper.Execution.PropertyDescription, AutoMapper.Execution.PropertyDescription)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.Execution.PropertyDescription.op_Inequality(AutoMapper.Execution.PropertyDescription, AutoMapper.Execution.PropertyDescription)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public AutoMapper.Execution.PropertyDescription[] AutoMapper.Execution.PropertyDescription[] AutoMapper.Execution.TypeDescription.AdditionalProperties' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Type System.Type AutoMapper.Execution.TypeDescription.Type' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void AutoMapper.Execution.TypeDescription..ctor(System.Type)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void AutoMapper.Execution.TypeDescription..ctor(System.Type, System.Collections.Generic.IEnumerable)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.Execution.TypeDescription.op_Equality(AutoMapper.Execution.TypeDescription, AutoMapper.Execution.TypeDescription)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.Execution.TypeDescription.op_Inequality(AutoMapper.Execution.TypeDescription, AutoMapper.Execution.TypeDescription)' does not exist in the implementation but it does exist in the contract.
+CannotSealType : Type 'AutoMapper.Execution.TypeMapPlanBuilder' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
+TypeCannotChangeClassification : Type 'AutoMapper.Execution.TypeMapPlanBuilder' is a 'struct' in the implementation but is a 'class' in the contract.
+MembersMustExist : Member 'public System.Linq.Expressions.LambdaExpression AutoMapper.Execution.TypeMapPlanBuilder.CreateMapperLambda(System.Collections.Generic.HashSet)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Linq.Expressions.ParameterExpression AutoMapper.Execution.TypeMapPlanBuilder.Source.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Collections.Generic.IEnumerator AutoMapper.Features.Features.GetEnumerator()' does not exist in the implementation but it does exist in the contract.
+TypesMustExist : Type 'AutoMapper.Internal.ConcurrentDictionaryWrapper' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Reflection.ConstructorInfo System.Reflection.ConstructorInfo AutoMapper.Internal.ConstructorParameters.Constructor' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Reflection.ParameterInfo[] System.Reflection.ParameterInfo[] AutoMapper.Internal.ConstructorParameters.Parameters' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public AutoMapper.Internal.TypePair AutoMapper.Internal.TypePair AutoMapper.Internal.MapRequest.RequestedTypes' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public AutoMapper.Internal.TypePair AutoMapper.Internal.TypePair AutoMapper.Internal.MapRequest.RuntimeTypes' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public AutoMapper.MemberMap AutoMapper.MemberMap AutoMapper.Internal.MapRequest.MemberMap' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.Internal.MapRequest.op_Equality(AutoMapper.Internal.MapRequest, AutoMapper.Internal.MapRequest)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.Internal.MapRequest.op_Inequality(AutoMapper.Internal.MapRequest, AutoMapper.Internal.MapRequest)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Collections.Generic.IEnumerable AutoMapper.Internal.TypeExtensions.GetDeclaredConstructors(System.Type)' does not exist in the implementation but it does exist in the contract.
+CannotChangeAttribute : Attribute 'System.Runtime.CompilerServices.IteratorStateMachineAttribute' on 'AutoMapper.Internal.TypeExtensions.GetTypeInheritance(System.Type)' changed from '[IteratorStateMachineAttribute(typeof(TypeExtensions.d__19))]' in the contract to '[IteratorStateMachineAttribute(typeof(TypeExtensions.d__22))]' in the implementation.
+MembersMustExist : Member 'public System.Type System.Type AutoMapper.Internal.TypePair.DestinationType' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Type System.Type AutoMapper.Internal.TypePair.SourceType' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.Internal.TypePair.Equals(AutoMapper.Internal.TypePair)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.Internal.TypePair.IsGenericTypeDefinition.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.Internal.TypePair.op_Equality(AutoMapper.Internal.TypePair, AutoMapper.Internal.TypePair)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.Boolean AutoMapper.Internal.TypePair.op_Inequality(AutoMapper.Internal.TypePair, AutoMapper.Internal.TypePair)' does not exist in the implementation but it does exist in the contract.
+CannotRemoveBaseTypeOrInterface : Type 'AutoMapper.Internal.Mappers.CollectionMapper' does not implement interface 'AutoMapper.Internal.Mappers.IObjectMapperInfo' in the implementation but it does in the contract.
+MembersMustExist : Member 'public AutoMapper.Internal.TypePair AutoMapper.Internal.Mappers.CollectionMapper.GetAssociatedTypes(AutoMapper.Internal.TypePair)' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Default interface member 'public System.Nullable AutoMapper.Internal.Mappers.IObjectMapper.GetAssociatedTypes(AutoMapper.Internal.TypePair)' is present in the implementation but not in the contract.
+TypesMustExist : Type 'AutoMapper.Internal.Mappers.IObjectMapperInfo' does not exist in the implementation but it does exist in the contract.
+CannotRemoveBaseTypeOrInterface : Type 'AutoMapper.Internal.Mappers.NullableDestinationMapper' does not implement interface 'AutoMapper.Internal.Mappers.IObjectMapperInfo' in the implementation but it does in the contract.
+MembersMustExist : Member 'public AutoMapper.Internal.TypePair AutoMapper.Internal.Mappers.NullableDestinationMapper.GetAssociatedTypes(AutoMapper.Internal.TypePair)' does not exist in the implementation but it does exist in the contract.
+CannotRemoveBaseTypeOrInterface : Type 'AutoMapper.Internal.Mappers.NullableSourceMapper' does not implement interface 'AutoMapper.Internal.Mappers.IObjectMapperInfo' in the implementation but it does in the contract.
+MembersMustExist : Member 'public AutoMapper.Internal.TypePair AutoMapper.Internal.Mappers.NullableSourceMapper.GetAssociatedTypes(AutoMapper.Internal.TypePair)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Linq.IQueryable AutoMapper.QueryableExtensions.Extensions.Map(System.Linq.IQueryable, System.Linq.IQueryable, AutoMapper.IConfigurationProvider)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyCollection AutoMapper.QueryableExtensions.MemberVisitor.MemberPath.get()' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'AutoMapper.QueryableExtensions.Impl.MemberAccessQueryMapperVisitor' does not exist in the implementation but it does exist in the contract.
-Total Issues: 18
+Total Issues: 123
diff --git a/src/AutoMapper/AssemblyInfo.cs b/src/AutoMapper/AssemblyInfo.cs
index c08097dc7e..922c8e4e0c 100644
--- a/src/AutoMapper/AssemblyInfo.cs
+++ b/src/AutoMapper/AssemblyInfo.cs
@@ -1,7 +1,10 @@
-using System;
-using System.Resources;
+using System.Resources;
using System.Runtime.InteropServices;
[assembly: CLSCompliant(true)]
[assembly: ComVisible(false)]
-[assembly: NeutralResourcesLanguage("en")]
\ No newline at end of file
+[assembly: NeutralResourcesLanguage("en")]
+
+namespace System.Runtime.CompilerServices;
+
+static class IsExternalInit { }
\ No newline at end of file
diff --git a/src/AutoMapper/AutoMapper.csproj b/src/AutoMapper/AutoMapper.csproj
index 97045c73a6..1172b2f71f 100644
--- a/src/AutoMapper/AutoMapper.csproj
+++ b/src/AutoMapper/AutoMapper.csproj
@@ -47,5 +47,10 @@
-
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AutoMapper/AutoMapperMappingException.cs b/src/AutoMapper/AutoMapperMappingException.cs
index 85ee5ea676..55afd1ed74 100644
--- a/src/AutoMapper/AutoMapperMappingException.cs
+++ b/src/AutoMapper/AutoMapperMappingException.cs
@@ -1,230 +1,226 @@
-using System;
using System.Text;
-using System.Linq;
-using AutoMapper.Internal;
-namespace AutoMapper
+namespace AutoMapper;
+
+///
+/// Wraps mapping exceptions. Check exception.ToString() for the full error message.
+///
+public class AutoMapperMappingException : Exception
{
- ///
- /// Wraps mapping exceptions. Check exception.ToString() for the full error message.
- ///
- public class AutoMapperMappingException : Exception
- {
- private readonly string _message;
+ private readonly string _message;
- public AutoMapperMappingException()
- {
- }
+ public AutoMapperMappingException()
+ {
+ }
- public AutoMapperMappingException(string message)
- : base(message) => _message = message;
+ public AutoMapperMappingException(string message)
+ : base(message) => _message = message;
- public AutoMapperMappingException(string message, Exception innerException)
- : base(message, innerException) => _message = message;
+ public AutoMapperMappingException(string message, Exception innerException)
+ : base(message, innerException) => _message = message;
- public AutoMapperMappingException(string message, Exception innerException, TypePair types)
- : this(message, innerException) => Types = types;
+ public AutoMapperMappingException(string message, Exception innerException, TypePair types)
+ : this(message, innerException) => Types = types;
- public AutoMapperMappingException(string message, Exception innerException, TypeMap typeMap)
- : this(message, innerException, typeMap.Types) => TypeMap = typeMap;
+ public AutoMapperMappingException(string message, Exception innerException, TypeMap typeMap)
+ : this(message, innerException, typeMap.Types) => TypeMap = typeMap;
- public AutoMapperMappingException(string message, Exception innerException, MemberMap memberMap)
- : this(message, innerException, memberMap.TypeMap) => MemberMap = memberMap;
+ public AutoMapperMappingException(string message, Exception innerException, MemberMap memberMap)
+ : this(message, innerException, memberMap.TypeMap) => MemberMap = memberMap;
- public TypePair? Types { get; set; }
- public TypeMap TypeMap { get; set; }
- public MemberMap MemberMap { get; set; }
+ public TypePair? Types { get; set; }
+ public TypeMap TypeMap { get; set; }
+ public MemberMap MemberMap { get; set; }
- public override string Message
+ public override string Message
+ {
+ get
{
- get
+ var message = _message;
+ var newLine = Environment.NewLine;
+ if (Types.HasValue && Types.Value.SourceType != null && Types.Value.DestinationType != null)
{
- var message = _message;
- var newLine = Environment.NewLine;
- if (Types.HasValue && Types.Value.SourceType != null && Types.Value.DestinationType != null)
- {
- message = message + newLine + newLine + "Mapping types:";
- message += newLine + $"{Types.Value.SourceType.Name} -> {Types.Value.DestinationType.Name}";
- message += newLine + $"{Types.Value.SourceType.FullName} -> {Types.Value.DestinationType.FullName}";
- }
- if (TypeMap != null)
- {
- message = message + newLine + newLine + "Type Map configuration:";
- message += newLine + $"{TypeMap.SourceType.Name} -> {TypeMap.DestinationType.Name}";
- message += newLine + $"{TypeMap.SourceType.FullName} -> {TypeMap.DestinationType.FullName}";
- }
- if (MemberMap != null)
- {
- message = message + newLine + newLine + "Destination Member:";
- message += newLine + $"{MemberMap}" + newLine;
- }
-
- return message;
+ message = message + newLine + newLine + "Mapping types:";
+ message += newLine + $"{Types.Value.SourceType.Name} -> {Types.Value.DestinationType.Name}";
+ message += newLine + $"{Types.Value.SourceType.FullName} -> {Types.Value.DestinationType.FullName}";
}
- }
-
-#if !DEBUG
- public override string StackTrace
- {
- get
+ if (TypeMap != null)
{
- return string.Join(Environment.NewLine,
- base.StackTrace
- .Split(new[] {Environment.NewLine}, StringSplitOptions.None)
- .Where(str => !str.TrimStart().StartsWith("at AutoMapper.")));
+ message = message + newLine + newLine + "Type Map configuration:";
+ message += newLine + $"{TypeMap.SourceType.Name} -> {TypeMap.DestinationType.Name}";
+ message += newLine + $"{TypeMap.SourceType.FullName} -> {TypeMap.DestinationType.FullName}";
}
- }
-#endif
- }
- public class DuplicateTypeMapConfigurationException : Exception
- {
- public TypeMapConfigErrors[] Errors { get; }
-
- public DuplicateTypeMapConfigurationException(TypeMapConfigErrors[] errors)
- {
- Errors = errors;
- var builder = new StringBuilder();
- builder.AppendLine("The following type maps were found in multiple profiles:");
- foreach (var error in Errors)
+ if (MemberMap != null)
{
- builder.AppendLine($"{error.Types.SourceType.FullName} to {error.Types.DestinationType.FullName} defined in profiles:");
- builder.AppendLine(string.Join(Environment.NewLine, error.ProfileNames));
+ message = message + newLine + newLine + "Destination Member:";
+ message += newLine + $"{MemberMap}" + newLine;
}
- builder.AppendLine("This can cause configuration collisions and inconsistent mapping.");
- builder.AppendLine("Consolidate the CreateMap calls into one profile, or set the root Internal().AllowAdditiveTypeMapCreation configuration value to 'true'.");
- Message = builder.ToString();
+ return message;
}
+ }
- public class TypeMapConfigErrors
+#if !DEBUG
+ public override string StackTrace
+ {
+ get
{
- public string[] ProfileNames { get; }
- public TypePair Types { get; }
+ return string.Join(Environment.NewLine,
+ base.StackTrace
+ .Split(new[] {Environment.NewLine}, StringSplitOptions.None)
+ .Where(str => !str.TrimStart().StartsWith("at AutoMapper.")));
+ }
+ }
+#endif
+}
+public class DuplicateTypeMapConfigurationException : Exception
+{
+ public TypeMapConfigErrors[] Errors { get; }
- public TypeMapConfigErrors(TypePair types, string[] profileNames)
- {
- Types = types;
- ProfileNames = profileNames;
- }
+ public DuplicateTypeMapConfigurationException(TypeMapConfigErrors[] errors)
+ {
+ Errors = errors;
+ var builder = new StringBuilder();
+ builder.AppendLine("The following type maps were found in multiple profiles:");
+ foreach (var error in Errors)
+ {
+ builder.AppendLine($"{error.Types.SourceType.FullName} to {error.Types.DestinationType.FullName} defined in profiles:");
+ builder.AppendLine(string.Join(Environment.NewLine, error.ProfileNames));
}
+ builder.AppendLine("This can cause configuration collisions and inconsistent mapping.");
+ builder.AppendLine("Consolidate the CreateMap calls into one profile, or set the root Internal().AllowAdditiveTypeMapCreation configuration value to 'true'.");
- public override string Message { get; }
+ Message = builder.ToString();
}
- public class AutoMapperConfigurationException : Exception
+
+ public class TypeMapConfigErrors
{
- public TypeMapConfigErrors[] Errors { get; }
- public TypePair? Types { get; }
- public MemberMap MemberMap { get; set; }
+ public string[] ProfileNames { get; }
+ public TypePair Types { get; }
- public class TypeMapConfigErrors
+ public TypeMapConfigErrors(TypePair types, string[] profileNames)
{
- public TypeMap TypeMap { get; }
- public string[] UnmappedPropertyNames { get; }
- public bool CanConstruct { get; }
-
- public TypeMapConfigErrors(TypeMap typeMap, string[] unmappedPropertyNames, bool canConstruct)
- {
- TypeMap = typeMap;
- UnmappedPropertyNames = unmappedPropertyNames;
- CanConstruct = canConstruct;
- }
+ Types = types;
+ ProfileNames = profileNames;
}
+ }
- public AutoMapperConfigurationException(string message)
- : base(message)
- {
- }
+ public override string Message { get; }
+}
+public class AutoMapperConfigurationException : Exception
+{
+ public TypeMapConfigErrors[] Errors { get; }
+ public TypePair? Types { get; }
+ public MemberMap MemberMap { get; set; }
- public AutoMapperConfigurationException(string message, Exception inner)
- : base(message, inner)
+ public class TypeMapConfigErrors
+ {
+ public TypeMap TypeMap { get; }
+ public string[] UnmappedPropertyNames { get; }
+ public bool CanConstruct { get; }
+
+ public TypeMapConfigErrors(TypeMap typeMap, string[] unmappedPropertyNames, bool canConstruct)
{
+ TypeMap = typeMap;
+ UnmappedPropertyNames = unmappedPropertyNames;
+ CanConstruct = canConstruct;
}
+ }
- public AutoMapperConfigurationException(TypeMapConfigErrors[] errors) => Errors = errors;
+ public AutoMapperConfigurationException(string message)
+ : base(message)
+ {
+ }
+
+ public AutoMapperConfigurationException(string message, Exception inner)
+ : base(message, inner)
+ {
+ }
- public AutoMapperConfigurationException(TypePair types) => Types = types;
+ public AutoMapperConfigurationException(TypeMapConfigErrors[] errors) => Errors = errors;
- public override string Message
+ public AutoMapperConfigurationException(TypePair types) => Types = types;
+
+ public override string Message
+ {
+ get
{
- get
+ if (Types.HasValue)
{
- if (Types.HasValue)
- {
- var message =
- string.Format(
- "The following member on {0} cannot be mapped: \n\t{2} \nAdd a custom mapping expression, ignore, add a custom resolver, or modify the destination type {1}.",
- Types.Value.DestinationType.FullName, Types.Value.DestinationType.FullName,
- MemberMap?.DestinationName);
+ var message =
+ string.Format(
+ "The following member on {0} cannot be mapped: \n\t{2} \nAdd a custom mapping expression, ignore, add a custom resolver, or modify the destination type {1}.",
+ Types.Value.DestinationType.FullName, Types.Value.DestinationType.FullName,
+ MemberMap);
- message += "\nContext:";
+ message += "\nContext:";
- Exception exToUse = this;
- while (exToUse != null)
+ Exception exToUse = this;
+ while (exToUse != null)
+ {
+ if (exToUse is AutoMapperConfigurationException configExc)
{
- if (exToUse is AutoMapperConfigurationException configExc)
- {
- message += configExc.MemberMap == null
- ? $"\n\tMapping from type {configExc.Types.Value.SourceType.FullName} to {configExc.Types.Value.DestinationType.FullName}"
- : $"\n\tMapping to member {configExc.MemberMap.DestinationName} from {configExc.Types.Value.SourceType.FullName} to {configExc.Types.Value.DestinationType.FullName}";
- }
-
- exToUse = exToUse.InnerException;
+ message += configExc.MemberMap == null
+ ? $"\n\tMapping from type {configExc.Types.Value.SourceType.FullName} to {configExc.Types.Value.DestinationType.FullName}"
+ : $"\n\tMapping to member {configExc.MemberMap} from {configExc.Types.Value.SourceType.FullName} to {configExc.Types.Value.DestinationType.FullName}";
}
- return message + "\n" + base.Message;
+ exToUse = exToUse.InnerException;
}
- if (Errors != null)
- {
- var message =
- new StringBuilder(
- "\nUnmapped members were found. Review the types and members below.\nAdd a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type\nFor no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters\n");
- foreach (var error in Errors)
+ return message + "\n" + base.Message;
+ }
+ if (Errors != null)
+ {
+ var message =
+ new StringBuilder(
+ "\nUnmapped members were found. Review the types and members below.\nAdd a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type\nFor no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters\n");
+
+ foreach (var error in Errors)
+ {
+ var len = error.TypeMap.SourceType.FullName.Length +
+ error.TypeMap.DestinationType.FullName.Length + 5;
+
+ message.AppendLine(new string('=', len));
+ message.AppendLine(error.TypeMap.SourceType.Name + " -> " + error.TypeMap.DestinationType.Name +
+ " (" +
+ error.TypeMap.ConfiguredMemberList + " member list)");
+ message.AppendLine(error.TypeMap.SourceType.FullName + " -> " +
+ error.TypeMap.DestinationType.FullName + " (" +
+ error.TypeMap.ConfiguredMemberList + " member list)");
+ message.AppendLine();
+
+ if (error.UnmappedPropertyNames.Any())
{
- var len = error.TypeMap.SourceType.FullName.Length +
- error.TypeMap.DestinationType.FullName.Length + 5;
-
- message.AppendLine(new string('=', len));
- message.AppendLine(error.TypeMap.SourceType.Name + " -> " + error.TypeMap.DestinationType.Name +
- " (" +
- error.TypeMap.ConfiguredMemberList + " member list)");
- message.AppendLine(error.TypeMap.SourceType.FullName + " -> " +
- error.TypeMap.DestinationType.FullName + " (" +
- error.TypeMap.ConfiguredMemberList + " member list)");
- message.AppendLine();
-
- if (error.UnmappedPropertyNames.Any())
- {
- message.AppendLine("Unmapped properties:");
- foreach (var name in error.UnmappedPropertyNames)
- {
- message.AppendLine(name);
- }
- }
- if (!error.CanConstruct)
+ message.AppendLine("Unmapped properties:");
+ foreach (var name in error.UnmappedPropertyNames)
{
- message.AppendLine("No available constructor.");
+ message.AppendLine(name);
}
}
- return message.ToString();
+ if (!error.CanConstruct)
+ {
+ message.AppendLine("No available constructor.");
+ }
}
- return base.Message;
+ return message.ToString();
}
+ return base.Message;
}
+ }
- public override string StackTrace
+ public override string StackTrace
+ {
+ get
{
- get
- {
- if (Errors != null)
- return string.Join(Environment.NewLine,
- base.StackTrace
- .Split(new[] { Environment.NewLine }, StringSplitOptions.None)
- .Where(str => !str.TrimStart().StartsWith("at AutoMapper."))
- .ToArray());
-
- return base.StackTrace;
- }
+ if (Errors != null)
+ return string.Join(Environment.NewLine,
+ base.StackTrace
+ .Split(new[] { Environment.NewLine }, StringSplitOptions.None)
+ .Where(str => !str.TrimStart().StartsWith("at AutoMapper."))
+ .ToArray());
+
+ return base.StackTrace;
}
}
}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/Annotations/AutoMapAttribute.cs b/src/AutoMapper/Configuration/Annotations/AutoMapAttribute.cs
index cfa836ed65..903867d05f 100644
--- a/src/AutoMapper/Configuration/Annotations/AutoMapAttribute.cs
+++ b/src/AutoMapper/Configuration/Annotations/AutoMapAttribute.cs
@@ -1,96 +1,93 @@
-using System;
-
-namespace AutoMapper
+namespace AutoMapper;
+
+///
+/// Auto map to this destination type from the specified source type.
+/// Discovered during scanning assembly scanning for configuration when calling
+///
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple = true)]
+public sealed class AutoMapAttribute : Attribute
{
+ public AutoMapAttribute(Type sourceType)
+ => SourceType = sourceType;
+
+ public Type SourceType { get; }
+ public bool ReverseMap { get; set; }
+
+ ///
+ /// If set to true, construct the destination object using the service locator.
+ ///
+ public bool ConstructUsingServiceLocator { get; set; }
+
+ ///
+ /// For self-referential types, limit recurse depth.
+ ///
+ public int MaxDepth { get; set; }
+
+ ///
+ /// If set to true, preserve object identity. Useful for circular references.
+ ///
+ public bool PreserveReferences { get; set; }
+
+ ///
+ /// If set to true, disable constructor validation.
+ ///
+ public bool DisableCtorValidation { get; set; }
+
///
- /// Auto map to this destination type from the specified source type.
- /// Discovered during scanning assembly scanning for configuration when calling
+ /// If set to true, include this configuration in all derived types' maps.
///
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple = true)]
- public sealed class AutoMapAttribute : Attribute
+ public bool IncludeAllDerived { get; set; }
+
+ ///
+ /// Skip normal member mapping and convert using a instantiated during mapping.
+ ///
+ public Type TypeConverter { get; set; }
+
+ ///
+ /// If set to true, proxy will be created.
+ ///
+ public bool AsProxy { get; set; }
+
+ public void ApplyConfiguration(IMappingExpression mappingExpression)
{
- public AutoMapAttribute(Type sourceType)
- => SourceType = sourceType;
-
- public Type SourceType { get; }
- public bool ReverseMap { get; set; }
-
- ///
- /// If set to true, construct the destination object using the service locator.
- ///
- public bool ConstructUsingServiceLocator { get; set; }
-
- ///
- /// For self-referential types, limit recurse depth.
- ///
- public int MaxDepth { get; set; }
-
- ///
- /// If set to true, preserve object identity. Useful for circular references.
- ///
- public bool PreserveReferences { get; set; }
-
- ///
- /// If set to true, disable constructor validation.
- ///
- public bool DisableCtorValidation { get; set; }
-
- ///
- /// If set to true, include this configuration in all derived types' maps.
- ///
- public bool IncludeAllDerived { get; set; }
-
- ///
- /// Skip normal member mapping and convert using a instantiated during mapping.
- ///
- public Type TypeConverter { get; set; }
-
- ///
- /// If set to true, proxy will be created.
- ///
- public bool AsProxy { get; set; }
-
- public void ApplyConfiguration(IMappingExpression mappingExpression)
+ if (ReverseMap)
+ {
+ mappingExpression.ReverseMap();
+ }
+
+ if (ConstructUsingServiceLocator)
+ {
+ mappingExpression.ConstructUsingServiceLocator();
+ }
+
+ if (MaxDepth > 0)
+ {
+ mappingExpression.MaxDepth(MaxDepth);
+ }
+
+ if (PreserveReferences)
+ {
+ mappingExpression.PreserveReferences();
+ }
+
+ if (DisableCtorValidation)
+ {
+ mappingExpression.DisableCtorValidation();
+ }
+
+ if (IncludeAllDerived)
+ {
+ mappingExpression.IncludeAllDerived();
+ }
+
+ if (TypeConverter != null)
+ {
+ mappingExpression.ConvertUsing(TypeConverter);
+ }
+
+ if (AsProxy)
{
- if (ReverseMap)
- {
- mappingExpression.ReverseMap();
- }
-
- if (ConstructUsingServiceLocator)
- {
- mappingExpression.ConstructUsingServiceLocator();
- }
-
- if (MaxDepth > 0)
- {
- mappingExpression.MaxDepth(MaxDepth);
- }
-
- if (PreserveReferences)
- {
- mappingExpression.PreserveReferences();
- }
-
- if (DisableCtorValidation)
- {
- mappingExpression.DisableCtorValidation();
- }
-
- if (IncludeAllDerived)
- {
- mappingExpression.IncludeAllDerived();
- }
-
- if (TypeConverter != null)
- {
- mappingExpression.ConvertUsing(TypeConverter);
- }
-
- if (AsProxy)
- {
- mappingExpression.AsProxy();
- }
+ mappingExpression.AsProxy();
}
}
}
diff --git a/src/AutoMapper/Configuration/Annotations/IgnoreAttribute.cs b/src/AutoMapper/Configuration/Annotations/IgnoreAttribute.cs
index de5ec40ade..cba7a708a2 100644
--- a/src/AutoMapper/Configuration/Annotations/IgnoreAttribute.cs
+++ b/src/AutoMapper/Configuration/Annotations/IgnoreAttribute.cs
@@ -1,19 +1,16 @@
-using System;
+namespace AutoMapper.Configuration.Annotations;
-namespace AutoMapper.Configuration.Annotations
+///
+/// Ignore this member for configuration validation and skip during mapping.
+///
+///
+/// Must be used in combination with
+///
+[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+public sealed class IgnoreAttribute : Attribute, IMemberConfigurationProvider
{
- ///
- /// Ignore this member for configuration validation and skip during mapping.
- ///
- ///
- /// Must be used in combination with
- ///
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public sealed class IgnoreAttribute : Attribute, IMemberConfigurationProvider
+ public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
{
- public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
- {
- memberConfigurationExpression.Ignore();
- }
+ memberConfigurationExpression.Ignore();
}
}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/Annotations/MapAtRuntimeAttribute.cs b/src/AutoMapper/Configuration/Annotations/MapAtRuntimeAttribute.cs
index 5d178b276f..8f89a78983 100644
--- a/src/AutoMapper/Configuration/Annotations/MapAtRuntimeAttribute.cs
+++ b/src/AutoMapper/Configuration/Annotations/MapAtRuntimeAttribute.cs
@@ -1,20 +1,17 @@
-using System;
+namespace AutoMapper.Configuration.Annotations;
-namespace AutoMapper.Configuration.Annotations
+///
+/// Do not precompute the execution plan for this member, just map it at runtime.
+/// Simplifies the execution plan by not inlining.
+///
+///
+/// Must be used in combination with
+///
+[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+public sealed class MapAtRuntimeAttribute : Attribute, IMemberConfigurationProvider
{
- ///
- /// Do not precompute the execution plan for this member, just map it at runtime.
- /// Simplifies the execution plan by not inlining.
- ///
- ///
- /// Must be used in combination with
- ///
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public sealed class MapAtRuntimeAttribute : Attribute, IMemberConfigurationProvider
+ public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
{
- public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
- {
- memberConfigurationExpression.MapAtRuntime();
- }
+ memberConfigurationExpression.MapAtRuntime();
}
}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/Annotations/MappingOrderAttribute.cs b/src/AutoMapper/Configuration/Annotations/MappingOrderAttribute.cs
index fd66142983..d028b1e906 100644
--- a/src/AutoMapper/Configuration/Annotations/MappingOrderAttribute.cs
+++ b/src/AutoMapper/Configuration/Annotations/MappingOrderAttribute.cs
@@ -1,26 +1,23 @@
-using System;
+namespace AutoMapper.Configuration.Annotations;
-namespace AutoMapper.Configuration.Annotations
+///
+/// Supply a custom mapping order instead of what the .NET runtime returns
+///
+///
+/// Must be used in combination with
+///
+[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+public sealed class MappingOrderAttribute : Attribute, IMemberConfigurationProvider
{
- ///
- /// Supply a custom mapping order instead of what the .NET runtime returns
- ///
- ///
- /// Must be used in combination with
- ///
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public sealed class MappingOrderAttribute : Attribute, IMemberConfigurationProvider
- {
- public int Value { get; }
+ public int Value { get; }
- public MappingOrderAttribute(int value)
- {
- Value = value;
- }
+ public MappingOrderAttribute(int value)
+ {
+ Value = value;
+ }
- public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
- {
- memberConfigurationExpression.SetMappingOrder(Value);
- }
+ public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
+ {
+ memberConfigurationExpression.SetMappingOrder(Value);
}
}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/Annotations/NullSubstituteAttribute.cs b/src/AutoMapper/Configuration/Annotations/NullSubstituteAttribute.cs
index a673520904..9de69f389c 100644
--- a/src/AutoMapper/Configuration/Annotations/NullSubstituteAttribute.cs
+++ b/src/AutoMapper/Configuration/Annotations/NullSubstituteAttribute.cs
@@ -1,29 +1,26 @@
-using System;
+namespace AutoMapper.Configuration.Annotations;
-namespace AutoMapper.Configuration.Annotations
+///
+/// Substitute a custom value when the source member resolves as null
+///
+///
+/// Must be used in combination with
+///
+[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+public sealed class NullSubstituteAttribute : Attribute, IMemberConfigurationProvider
{
///
- /// Substitute a custom value when the source member resolves as null
+ /// Value to use if source value is null
///
- ///
- /// Must be used in combination with
- ///
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public sealed class NullSubstituteAttribute : Attribute, IMemberConfigurationProvider
- {
- ///
- /// Value to use if source value is null
- ///
- public object Value { get; }
+ public object Value { get; }
- public NullSubstituteAttribute(object value)
- {
- Value = value;
- }
+ public NullSubstituteAttribute(object value)
+ {
+ Value = value;
+ }
- public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
- {
- memberConfigurationExpression.NullSubstitute(Value);
- }
+ public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
+ {
+ memberConfigurationExpression.NullSubstitute(Value);
}
}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/Annotations/SourceMemberAttribute.cs b/src/AutoMapper/Configuration/Annotations/SourceMemberAttribute.cs
index de1d7cbcdf..a73ac3756a 100644
--- a/src/AutoMapper/Configuration/Annotations/SourceMemberAttribute.cs
+++ b/src/AutoMapper/Configuration/Annotations/SourceMemberAttribute.cs
@@ -1,30 +1,25 @@
-using AutoMapper.Internal;
-using System;
-using System.Reflection;
+namespace AutoMapper.Configuration.Annotations;
-namespace AutoMapper.Configuration.Annotations
+///
+/// Specify the source member to map from. Can only reference a member on the type
+///
+///
+/// Must be used in combination with
+///
+[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+public sealed class SourceMemberAttribute : Attribute, IMemberConfigurationProvider
{
- ///
- /// Specify the source member to map from. Can only reference a member on the type
- ///
- ///
- /// Must be used in combination with
- ///
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public sealed class SourceMemberAttribute : Attribute, IMemberConfigurationProvider
- {
- public string Name { get; }
+ public string Name { get; }
- public SourceMemberAttribute(string name) => Name = name;
+ public SourceMemberAttribute(string name) => Name = name;
- public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
+ public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
+ {
+ var destinationMember = memberConfigurationExpression.DestinationMember;
+ if (destinationMember.Has() || destinationMember.Has())
{
- var destinationMember = memberConfigurationExpression.DestinationMember;
- if (destinationMember.Has() || destinationMember.Has())
- {
- return;
- }
- memberConfigurationExpression.MapFrom(Name);
+ return;
}
+ memberConfigurationExpression.MapFrom(Name);
}
}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/Annotations/UseExistingValueAttribute.cs b/src/AutoMapper/Configuration/Annotations/UseExistingValueAttribute.cs
index defd4131e1..b6c93f7c60 100644
--- a/src/AutoMapper/Configuration/Annotations/UseExistingValueAttribute.cs
+++ b/src/AutoMapper/Configuration/Annotations/UseExistingValueAttribute.cs
@@ -1,19 +1,16 @@
-using System;
+namespace AutoMapper.Configuration.Annotations;
-namespace AutoMapper.Configuration.Annotations
+///
+/// Use the destination value instead of mapping from the source value or creating a new instance
+///
+///
+/// Must be used in combination with
+///
+[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+public sealed class UseExistingValueAttribute : Attribute, IMemberConfigurationProvider
{
- ///
- /// Use the destination value instead of mapping from the source value or creating a new instance
- ///
- ///
- /// Must be used in combination with
- ///
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public sealed class UseExistingValueAttribute : Attribute, IMemberConfigurationProvider
+ public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
{
- public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
- {
- memberConfigurationExpression.UseDestinationValue();
- }
+ memberConfigurationExpression.UseDestinationValue();
}
}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/Annotations/ValueConverterAttribute.cs b/src/AutoMapper/Configuration/Annotations/ValueConverterAttribute.cs
index 6d3759782d..bf0a87e552 100644
--- a/src/AutoMapper/Configuration/Annotations/ValueConverterAttribute.cs
+++ b/src/AutoMapper/Configuration/Annotations/ValueConverterAttribute.cs
@@ -1,40 +1,36 @@
-using System;
-using System.Reflection;
+namespace AutoMapper.Configuration.Annotations;
-namespace AutoMapper.Configuration.Annotations
+///
+/// Specify a value converter type to convert from the matching source member to the destination member
+/// Use with to specify a separate source member to supply to the value converter
+///
+///
+/// Must be used in combination with
+///
+[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+public sealed class ValueConverterAttribute : Attribute, IMemberConfigurationProvider
{
///
- /// Specify a value converter type to convert from the matching source member to the destination member
- /// Use with to specify a separate source member to supply to the value converter
+ /// type
///
- ///
- /// Must be used in combination with
- ///
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public sealed class ValueConverterAttribute : Attribute, IMemberConfigurationProvider
+ public Type Type { get; }
+
+ public ValueConverterAttribute(Type type)
+ {
+ Type = type;
+ }
+
+ public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
{
- ///
- /// type
- ///
- public Type Type { get; }
+ var sourceMemberAttribute = memberConfigurationExpression.DestinationMember.GetCustomAttribute();
- public ValueConverterAttribute(Type type)
+ if (sourceMemberAttribute != null)
{
- Type = type;
+ memberConfigurationExpression.ConvertUsing(Type, sourceMemberAttribute.Name);
}
-
- public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
+ else
{
- var sourceMemberAttribute = memberConfigurationExpression.DestinationMember.GetCustomAttribute();
-
- if (sourceMemberAttribute != null)
- {
- memberConfigurationExpression.ConvertUsing(Type, sourceMemberAttribute.Name);
- }
- else
- {
- memberConfigurationExpression.ConvertUsing(Type);
- }
+ memberConfigurationExpression.ConvertUsing(Type);
}
}
}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/Annotations/ValueResolverAttribute.cs b/src/AutoMapper/Configuration/Annotations/ValueResolverAttribute.cs
index 5fd5bb4719..dea7196d35 100644
--- a/src/AutoMapper/Configuration/Annotations/ValueResolverAttribute.cs
+++ b/src/AutoMapper/Configuration/Annotations/ValueResolverAttribute.cs
@@ -1,40 +1,36 @@
-using System;
-using System.Reflection;
+namespace AutoMapper.Configuration.Annotations;
-namespace AutoMapper.Configuration.Annotations
+///
+/// Map destination member using a custom value resolver.
+/// Use with to specify an type.
+///
+///
+/// Must be used in combination with
+///
+[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+public sealed class ValueResolverAttribute : Attribute, IMemberConfigurationProvider
{
///
- /// Map destination member using a custom value resolver.
- /// Use with to specify an type.
+ /// or type
///
- ///
- /// Must be used in combination with
- ///
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public sealed class ValueResolverAttribute : Attribute, IMemberConfigurationProvider
+ public Type Type { get; }
+
+ public ValueResolverAttribute(Type type)
+ {
+ Type = type;
+ }
+
+ public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
{
- ///
- /// or type
- ///
- public Type Type { get; }
+ var sourceMemberAttribute = memberConfigurationExpression.DestinationMember.GetCustomAttribute();
- public ValueResolverAttribute(Type type)
+ if (sourceMemberAttribute != null)
{
- Type = type;
+ memberConfigurationExpression.MapFrom(Type, sourceMemberAttribute.Name);
}
-
- public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
+ else
{
- var sourceMemberAttribute = memberConfigurationExpression.DestinationMember.GetCustomAttribute();
-
- if (sourceMemberAttribute != null)
- {
- memberConfigurationExpression.MapFrom(Type, sourceMemberAttribute.Name);
- }
- else
- {
- memberConfigurationExpression.MapFrom(Type);
- }
+ memberConfigurationExpression.MapFrom(Type);
}
}
}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/ConfigurationValidator.cs b/src/AutoMapper/Configuration/ConfigurationValidator.cs
index cf889c3d2c..73139f3be5 100644
--- a/src/AutoMapper/Configuration/ConfigurationValidator.cs
+++ b/src/AutoMapper/Configuration/ConfigurationValidator.cs
@@ -1,183 +1,128 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using AutoMapper.Internal;
using AutoMapper.Internal.Mappers;
-namespace AutoMapper.Configuration
+namespace AutoMapper.Configuration;
+
+[EditorBrowsable(EditorBrowsableState.Never)]
+public readonly record struct ConfigurationValidator(IGlobalConfigurationExpression Expression)
{
- using Validator = Action;
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class ConfigurationValidator
+ private void Validate(ValidationContext context)
{
- private readonly IGlobalConfiguration _config;
- private readonly IGlobalConfigurationExpression _expression;
- private readonly Validator[] _validators;
-
- public ConfigurationValidator(IGlobalConfiguration config, IGlobalConfigurationExpression expression)
+ foreach (var validator in Expression.Validators)
{
- _validators = expression.GetValidators();
- _config = config;
- _expression = expression;
+ validator(context);
}
-
- private void Validate(ValidationContext context)
+ }
+ public void AssertConfigurationExpressionIsValid(IGlobalConfiguration config, IEnumerable typeMaps)
+ {
+ if (!Expression.AllowAdditiveTypeMapCreation)
{
- foreach (var validator in _validators)
+ var duplicateTypeMapConfigs = Expression.Profiles.Append((Profile)Expression)
+ .SelectMany(p => p.TypeMapConfigs, (profile, typeMap) => (profile, typeMap))
+ .GroupBy(x => x.typeMap.Types)
+ .Where(g => g.Count() > 1)
+ .Select(g => (TypePair : g.Key, ProfileNames : g.Select(tmc => tmc.profile.ProfileName).ToArray()))
+ .Select(g => new DuplicateTypeMapConfigurationException.TypeMapConfigErrors(g.TypePair, g.ProfileNames))
+ .ToArray();
+ if (duplicateTypeMapConfigs.Any())
{
- validator(context);
+ throw new DuplicateTypeMapConfigurationException(duplicateTypeMapConfigs);
}
}
-
- public void AssertConfigurationExpressionIsValid(IEnumerable typeMaps)
+ AssertConfigurationIsValid(config, typeMaps);
+ }
+ public void AssertConfigurationIsValid(IGlobalConfiguration config, IEnumerable typeMaps)
+ {
+ var maps = typeMaps as TypeMap[] ?? typeMaps.ToArray();
+ var badTypeMaps =
+ (from typeMap in maps
+ where typeMap.ShouldCheckForValid
+ let unmappedPropertyNames = typeMap.GetUnmappedPropertyNames()
+ let canConstruct = typeMap.PassesCtorValidation
+ where unmappedPropertyNames.Length > 0 || !canConstruct
+ select new AutoMapperConfigurationException.TypeMapConfigErrors(typeMap, unmappedPropertyNames, canConstruct)
+ ).ToArray();
+
+ if (badTypeMaps.Any())
{
- if (!_expression.AllowAdditiveTypeMapCreation)
- {
- var duplicateTypeMapConfigs = _expression.Profiles.Append((Profile)_expression)
- .SelectMany(p => p.TypeMapConfigs, (profile, typeMap) => (profile, typeMap))
- .GroupBy(x => x.typeMap.Types)
- .Where(g => g.Count() > 1)
- .Select(g => (TypePair : g.Key, ProfileNames : g.Select(tmc => tmc.profile.ProfileName).ToArray()))
- .Select(g => new DuplicateTypeMapConfigurationException.TypeMapConfigErrors(g.TypePair, g.ProfileNames))
- .ToArray();
- if (duplicateTypeMapConfigs.Any())
- {
- throw new DuplicateTypeMapConfigurationException(duplicateTypeMapConfigs);
- }
- }
- AssertConfigurationIsValid(typeMaps);
+ throw new AutoMapperConfigurationException(badTypeMaps);
}
-
- public void AssertConfigurationIsValid(IEnumerable typeMaps)
+ var typeMapsChecked = new HashSet();
+ var configExceptions = new List();
+ foreach (var typeMap in maps)
{
- var maps = typeMaps as TypeMap[] ?? typeMaps.ToArray();
- var badTypeMaps =
- (from typeMap in maps
- where typeMap.ShouldCheckForValid
- let unmappedPropertyNames = typeMap.GetUnmappedPropertyNames()
- let canConstruct = typeMap.PassesCtorValidation
- where unmappedPropertyNames.Length > 0 || !canConstruct
- select new AutoMapperConfigurationException.TypeMapConfigErrors(typeMap, unmappedPropertyNames, canConstruct)
- ).ToArray();
-
- if (badTypeMaps.Any())
+ try
{
- throw new AutoMapperConfigurationException(badTypeMaps);
+ DryRunTypeMap(config, typeMapsChecked, typeMap.Types, typeMap, null);
}
-
- var typeMapsChecked = new List();
- var configExceptions = new List();
-
- foreach (var typeMap in maps)
- {
- try
- {
- DryRunTypeMap(typeMapsChecked, typeMap.Types, typeMap, null);
- }
- catch (Exception e)
- {
- configExceptions.Add(e);
- }
- }
-
- if (configExceptions.Count > 1)
+ catch (Exception e)
{
- throw new AggregateException(configExceptions);
- }
- if (configExceptions.Count > 0)
- {
- throw configExceptions[0];
+ configExceptions.Add(e);
}
}
-
- private void DryRunTypeMap(ICollection typeMapsChecked, TypePair types, TypeMap typeMap, MemberMap memberMap)
+ if (configExceptions.Count > 1)
{
- if(typeMap == null)
+ throw new AggregateException(configExceptions);
+ }
+ if (configExceptions.Count > 0)
+ {
+ throw configExceptions[0];
+ }
+ }
+ private void DryRunTypeMap(IGlobalConfiguration config, HashSet typeMapsChecked, TypePair types, TypeMap typeMap, MemberMap memberMap)
+ {
+ if(typeMap == null)
+ {
+ if (types.ContainsGenericParameters)
{
- if (types.ContainsGenericParameters)
- {
- return;
- }
- typeMap = _config.ResolveTypeMap(types.SourceType, types.DestinationType);
+ return;
}
- if (typeMap != null)
+ typeMap = config.ResolveTypeMap(types.SourceType, types.DestinationType);
+ }
+ if (typeMap != null)
+ {
+ if (typeMapsChecked.Contains(typeMap))
{
- if (typeMapsChecked.Contains(typeMap))
- {
- return;
- }
- typeMapsChecked.Add(typeMap);
-
- var context = new ValidationContext(types, memberMap, typeMap);
- Validate(context);
-
- if(!typeMap.ShouldCheckForValid)
- {
- return;
- }
-
- CheckPropertyMaps(typeMapsChecked, typeMap);
+ return;
}
- else
+ typeMapsChecked.Add(typeMap);
+ var context = new ValidationContext(types, memberMap, typeMap);
+ Validate(context);
+ if(!typeMap.ShouldCheckForValid)
{
- var mapperToUse = _config.FindMapper(types);
- if (mapperToUse == null)
- {
- throw new AutoMapperConfigurationException(memberMap.TypeMap.Types) { MemberMap = memberMap };
- }
- var context = new ValidationContext(types, memberMap, mapperToUse);
- Validate(context);
- if(mapperToUse is IObjectMapperInfo mapperInfo)
- {
- var newTypePair = mapperInfo.GetAssociatedTypes(types);
- if (newTypePair != types)
- {
- DryRunTypeMap(typeMapsChecked, newTypePair, null, memberMap);
- }
- }
+ return;
}
+ CheckPropertyMaps(config, typeMapsChecked, typeMap);
}
-
- private void CheckPropertyMaps(ICollection typeMapsChecked, TypeMap typeMap)
+ else
{
- foreach (var memberMap in typeMap.MemberMaps)
+ var mapperToUse = config.FindMapper(types);
+ if (mapperToUse == null)
+ {
+ throw new AutoMapperConfigurationException(memberMap.TypeMap.Types) { MemberMap = memberMap };
+ }
+ var context = new ValidationContext(types, memberMap, ObjectMapper: mapperToUse);
+ Validate(context);
+ if (mapperToUse.GetAssociatedTypes(types) is TypePair newTypes && newTypes != types)
{
- if(memberMap.Ignored)
- {
- continue;
- }
- var sourceType = memberMap.SourceType;
- // when we don't know what the source type is, bail
- if (sourceType.IsGenericParameter || sourceType == typeof(object))
- {
- return;
- }
- var destinationType = memberMap.DestinationType;
- DryRunTypeMap(typeMapsChecked, new TypePair(sourceType, destinationType), null, memberMap);
+ DryRunTypeMap(config, typeMapsChecked, newTypes, null, memberMap);
}
}
}
- public readonly struct ValidationContext
+ private void CheckPropertyMaps(IGlobalConfiguration config, HashSet typeMapsChecked, TypeMap typeMap)
{
- public readonly IObjectMapper ObjectMapper { get; }
- public readonly MemberMap MemberMap { get; }
- public readonly TypeMap TypeMap { get; }
- public readonly TypePair Types { get; }
-
- public ValidationContext(TypePair types, MemberMap memberMap, IObjectMapper objectMapper) : this(types, memberMap, objectMapper, null)
- {
- }
-
- public ValidationContext(TypePair types, MemberMap memberMap, TypeMap typeMap) : this(types, memberMap, null, typeMap)
+ foreach (var memberMap in typeMap.MemberMaps)
{
- }
-
- private ValidationContext(TypePair types, MemberMap memberMap, IObjectMapper objectMapper, TypeMap typeMap)
- {
- ObjectMapper = objectMapper;
- TypeMap = typeMap;
- Types = types;
- MemberMap = memberMap;
+ if(memberMap.Ignored)
+ {
+ continue;
+ }
+ var sourceType = memberMap.SourceType;
+ // when we don't know what the source type is, bail
+ if (sourceType.IsGenericParameter || sourceType == typeof(object))
+ {
+ return;
+ }
+ DryRunTypeMap(config, typeMapsChecked, new(sourceType, memberMap.DestinationType), null, memberMap);
}
}
-}
\ No newline at end of file
+}
+public readonly record struct ValidationContext(TypePair Types, MemberMap MemberMap, TypeMap TypeMap = null, IObjectMapper ObjectMapper = null);
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/Conventions.cs b/src/AutoMapper/Configuration/Conventions.cs
new file mode 100644
index 0000000000..72c7734a15
--- /dev/null
+++ b/src/AutoMapper/Configuration/Conventions.cs
@@ -0,0 +1,200 @@
+namespace AutoMapper.Configuration.Conventions;
+public interface ISourceToDestinationNameMapper
+{
+ MemberInfo GetSourceMember(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch);
+ void Merge(ISourceToDestinationNameMapper other);
+}
+[EditorBrowsable(EditorBrowsableState.Never)]
+public class MemberConfiguration
+{
+ NameSplitMember _nameSplitMember;
+ public INamingConvention SourceNamingConvention { get; set; } = PascalCaseNamingConvention.Instance;
+ public INamingConvention DestinationNamingConvention { get; set; } = PascalCaseNamingConvention.Instance;
+ public List NameToMemberMappers { get; } = new();
+ public bool IsMatch(ProfileMap options, TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch, List resolvers, bool isReverseMap)
+ {
+ var matchingMemberInfo = GetSourceMember(sourceTypeDetails, destType, destMemberType, nameToSearch);
+ if (matchingMemberInfo != null)
+ {
+ resolvers.Add(matchingMemberInfo);
+ return true;
+ }
+ return nameToSearch.Length == 0 || _nameSplitMember.IsMatch(options, sourceTypeDetails, destType, destMemberType, nameToSearch, resolvers, isReverseMap);
+ }
+ public MemberInfo GetSourceMember(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch)
+ {
+ var sourceMember = sourceTypeDetails.GetMember(nameToSearch);
+ if (sourceMember != null)
+ {
+ return sourceMember;
+ }
+ foreach (var mapper in NameToMemberMappers)
+ {
+ if ((sourceMember = mapper.GetSourceMember(sourceTypeDetails, destType, destMemberType, nameToSearch)) != null)
+ {
+ return sourceMember;
+ }
+ }
+ return null;
+ }
+ public void Seal()
+ {
+ var isDefault = SourceNamingConvention == PascalCaseNamingConvention.Instance && DestinationNamingConvention == PascalCaseNamingConvention.Instance;
+ _nameSplitMember = isDefault ? new DefaultNameSplitMember() : new ConventionsNameSplitMember();
+ _nameSplitMember.Parent = this;
+ }
+ public void Merge(MemberConfiguration other)
+ {
+ if (other == null)
+ {
+ return;
+ }
+ var initialCount = NameToMemberMappers.Count;
+ for (int index = 0; index < other.NameToMemberMappers.Count; index++)
+ {
+ var otherMapper = other.NameToMemberMappers[index];
+ if (index < initialCount)
+ {
+ var nameToMemberMapper = NameToMemberMappers[index];
+ if (nameToMemberMapper.GetType() == otherMapper.GetType())
+ {
+ nameToMemberMapper.Merge(otherMapper);
+ continue;
+ }
+ }
+ NameToMemberMappers.Add(otherMapper);
+ }
+ }
+}
+public class PrePostfixName : ISourceToDestinationNameMapper
+{
+ public List DestinationPrefixes { get; } = new();
+ public List DestinationPostfixes { get; } = new();
+ public MemberInfo GetSourceMember(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch)
+ {
+ MemberInfo member;
+ foreach (var possibleSourceName in TypeDetails.PossibleNames(nameToSearch, DestinationPrefixes, DestinationPostfixes))
+ {
+ if ((member = sourceTypeDetails.GetMember(possibleSourceName)) != null)
+ {
+ return member;
+ }
+ }
+ return null;
+ }
+ public void Merge(ISourceToDestinationNameMapper other)
+ {
+ var typedOther = (PrePostfixName)other;
+ DestinationPrefixes.TryAdd(typedOther.DestinationPrefixes);
+ DestinationPostfixes.TryAdd(typedOther.DestinationPostfixes);
+ }
+}
+public class ReplaceName : ISourceToDestinationNameMapper
+{
+ public List MemberNameReplacers { get; } = new();
+ public MemberInfo GetSourceMember(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch)
+ {
+ var possibleSourceNames = PossibleNames(nameToSearch);
+ if (possibleSourceNames.Length == 0)
+ {
+ return null;
+ }
+ var possibleDestNames = sourceTypeDetails.ReadAccessors.Select(mi => (mi, possibles : PossibleNames(mi.Name))).ToArray();
+ foreach (var sourceName in possibleSourceNames)
+ {
+ foreach (var (mi, possibles) in possibleDestNames)
+ {
+ if (possibles.Contains(sourceName, StringComparer.OrdinalIgnoreCase))
+ {
+ return mi;
+ }
+ }
+ }
+ return null;
+ }
+ public void Merge(ISourceToDestinationNameMapper other)
+ {
+ var typedOther = (ReplaceName)other;
+ MemberNameReplacers.TryAdd(typedOther.MemberNameReplacers);
+ }
+ private string[] PossibleNames(string nameToSearch) =>
+ MemberNameReplacers.Select(r => nameToSearch.Replace(r.OriginalValue, r.NewValue))
+ .Concat(new[] { MemberNameReplacers.Aggregate(nameToSearch, (s, r) => s.Replace(r.OriginalValue, r.NewValue)), nameToSearch })
+ .ToArray();
+}
+[EditorBrowsable(EditorBrowsableState.Never)]
+public readonly record struct MemberNameReplacer(string OriginalValue, string NewValue);
+[EditorBrowsable(EditorBrowsableState.Never)]
+public abstract class NameSplitMember
+{
+ public MemberConfiguration Parent { get; set; }
+ public abstract bool IsMatch(ProfileMap options, TypeDetails sourceType, Type destType, Type destMemberType, string nameToSearch, List resolvers, bool isReverseMap);
+}
+[EditorBrowsable(EditorBrowsableState.Never)]
+public sealed class DefaultNameSplitMember : NameSplitMember
+{
+ public sealed override bool IsMatch(ProfileMap options, TypeDetails sourceType, Type destType, Type destMemberType, string nameToSearch, List resolvers, bool isReverseMap)
+ {
+ MemberInfo matchingMemberInfo = null;
+ int index = 1;
+ for (; index < nameToSearch.Length; index++)
+ {
+ if (char.IsUpper(nameToSearch[index]) && Found())
+ {
+ return true;
+ }
+ }
+ return matchingMemberInfo != null && Found();
+ bool Found()
+ {
+ var first = nameToSearch[..index];
+ matchingMemberInfo = Parent.GetSourceMember(sourceType, destType, destMemberType, first);
+ if (matchingMemberInfo == null)
+ {
+ return false;
+ }
+ resolvers.Add(matchingMemberInfo);
+ var second = nameToSearch[index..];
+ var details = options.CreateTypeDetails(matchingMemberInfo.GetMemberType());
+ if (Parent.IsMatch(options, details, destType, destMemberType, second, resolvers, isReverseMap))
+ {
+ return true;
+ }
+ resolvers.RemoveAt(resolvers.Count - 1);
+ return false;
+ }
+ }
+}
+[EditorBrowsable(EditorBrowsableState.Never)]
+public sealed class ConventionsNameSplitMember : NameSplitMember
+{
+ public sealed override bool IsMatch(ProfileMap options, TypeDetails sourceType, Type destType, Type destMemberType, string nameToSearch, List resolvers, bool isReverseMap)
+ {
+ var destinationNamingConvention = isReverseMap ? Parent.SourceNamingConvention : Parent.DestinationNamingConvention;
+ var matches = destinationNamingConvention.Split(nameToSearch);
+ var length = matches.Length;
+ if (length < 2)
+ {
+ return false;
+ }
+ var sourceNamingConvention = isReverseMap ? Parent.DestinationNamingConvention : Parent.SourceNamingConvention;
+ var separator = sourceNamingConvention.SeparatorCharacter;
+ for (var index = 1; index <= length; index++)
+ {
+ var first = string.Join(separator, matches, 0, index);
+ var matchingMemberInfo = Parent.GetSourceMember(sourceType, destType, destMemberType, first);
+ if (matchingMemberInfo != null)
+ {
+ resolvers.Add(matchingMemberInfo);
+ var second = string.Join(separator, matches, index, length - index);
+ var details = options.CreateTypeDetails(matchingMemberInfo.GetMemberType());
+ if (Parent.IsMatch(options, details, destType, destMemberType, second, resolvers, isReverseMap))
+ {
+ return true;
+ }
+ resolvers.RemoveAt(resolvers.Count - 1);
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/Conventions/Mappers.cs b/src/AutoMapper/Configuration/Conventions/Mappers.cs
deleted file mode 100644
index 938fb1cef7..0000000000
--- a/src/AutoMapper/Configuration/Conventions/Mappers.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using AutoMapper.Internal;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-namespace AutoMapper.Configuration.Conventions
-{
- public interface ISourceToDestinationNameMapper
- {
- MemberInfo GetMatchingMemberInfo(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch);
- }
- public interface IParentSourceToDestinationNameMapper
- {
- List NamedMappers { get; }
- MemberInfo GetMatchingMemberInfo(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch);
- }
- public sealed class DefaultName : ISourceToDestinationNameMapper
- {
- public MemberInfo GetMatchingMemberInfo(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch) => sourceTypeDetails.GetMember(nameToSearch);
- }
- public class ParentSourceToDestinationNameMapper : IParentSourceToDestinationNameMapper
- {
- public List NamedMappers { get; } = new List { new DefaultName() };
- public MemberInfo GetMatchingMemberInfo(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch)
- {
- MemberInfo memberInfo = null;
- foreach (var namedMapper in NamedMappers)
- {
- memberInfo = namedMapper.GetMatchingMemberInfo(sourceTypeDetails, destType, destMemberType, nameToSearch);
- if (memberInfo != null)
- break;
- }
- return memberInfo;
- }
- }
- public class PrePostfixName : ISourceToDestinationNameMapper
- {
- public List Prefixes { get; } = new List();
- public List Postfixes { get; } = new List();
- public List DestinationPrefixes { get; } = new List();
- public List DestinationPostfixes { get; } = new List();
- public MemberInfo GetMatchingMemberInfo(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch)
- {
- MemberInfo member;
- foreach (var possibleSourceName in TypeDetails.PossibleNames(nameToSearch, DestinationPrefixes, DestinationPostfixes))
- {
- if ((member = sourceTypeDetails.GetMember(possibleSourceName)) != null)
- {
- return member;
- }
- }
- return null;
- }
- }
- public class ReplaceName : ISourceToDestinationNameMapper
- {
- private readonly List _memberNameReplacers = new List();
-
- public ReplaceName AddReplace(string original, string newValue)
- {
- _memberNameReplacers.Add(new MemberNameReplacer(original, newValue));
- return this;
- }
- public MemberInfo GetMatchingMemberInfo(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch)
- {
- var possibleSourceNames = PossibleNames(nameToSearch);
- var possibleDestNames = sourceTypeDetails.ReadAccessors.Select(mi => (mi, possibles : PossibleNames(mi.Name))).ToArray();
- foreach (var sourceName in possibleSourceNames)
- {
- foreach (var destName in possibleDestNames)
- {
- if (Array.Exists(destName.possibles, name => string.Compare(name, sourceName, StringComparison.OrdinalIgnoreCase) == 0))
- {
- return destName.mi;
- }
- }
- }
- return null;
- }
- private string[] PossibleNames(string nameToSearch) =>
- _memberNameReplacers.Select(r => nameToSearch.Replace(r.OriginalValue, r.NewValue))
- .Concat(new[] { _memberNameReplacers.Aggregate(nameToSearch, (s, r) => s.Replace(r.OriginalValue, r.NewValue)), nameToSearch })
- .ToArray();
- }
- public class MemberNameReplacer
- {
- public MemberNameReplacer(string originalValue, string newValue)
- {
- OriginalValue = originalValue;
- NewValue = newValue;
- }
-
- public string OriginalValue { get; }
- public string NewValue { get; }
- }
-}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/Conventions/MemberConfiguration.cs b/src/AutoMapper/Configuration/Conventions/MemberConfiguration.cs
deleted file mode 100644
index bfbb41e92f..0000000000
--- a/src/AutoMapper/Configuration/Conventions/MemberConfiguration.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-using AutoMapper.Internal;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Reflection;
-using System.Text.RegularExpressions;
-
-namespace AutoMapper.Configuration.Conventions
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- public interface IMemberConfiguration
- {
- List MemberMappers { get; }
- IMemberConfiguration AddMember(Action setupAction = null)
- where TMemberMapper : IChildMemberConfiguration, new();
-
- IMemberConfiguration AddName(Action setupAction = null)
- where TNameMapper : ISourceToDestinationNameMapper, new();
-
- IParentSourceToDestinationNameMapper NameMapper { get; set; }
- bool MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceType, Type destType, Type destMemberType, string nameToSearch, List resolvers, bool isReverseMap);
- }
- [EditorBrowsable(EditorBrowsableState.Never)]
- public interface IChildMemberConfiguration
- {
- bool MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceType, Type destType, Type destMemberType, string nameToSearch, List resolvers, IMemberConfiguration parent, bool isReverseMap);
- }
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class DefaultMember : IChildMemberConfiguration
- {
- public IParentSourceToDestinationNameMapper NameMapper { get; set; }
-
- public bool MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch, List resolvers, IMemberConfiguration parent = null, bool isReverseMap = false)
- {
- var matchingMemberInfo = NameMapper.GetMatchingMemberInfo(sourceTypeDetails, destType, destMemberType, nameToSearch);
- if (matchingMemberInfo != null)
- {
- resolvers.Add(matchingMemberInfo);
- return true;
- }
- return nameToSearch.Length == 0;
- }
- }
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class MemberConfiguration : IMemberConfiguration
- {
- public IParentSourceToDestinationNameMapper NameMapper { get; set; }
-
- public List MemberMappers { get; } = new List();
-
- public IMemberConfiguration AddMember(Action setupAction = null)
- where TMemberMapper : IChildMemberConfiguration, new()
- {
- GetOrAdd(_ => (IList)_.MemberMappers, setupAction);
- return this;
- }
-
- public IMemberConfiguration AddName(Action setupAction = null)
- where TNameMapper : ISourceToDestinationNameMapper, new()
- {
- GetOrAdd(_ => (IList)_.NameMapper.NamedMappers, setupAction);
- return this;
- }
-
- private TMemberMapper GetOrAdd(Func getList, Action setupAction = null)
- where TMemberMapper : new()
- {
- var child = getList(this).OfType().FirstOrDefault();
- if (child == null)
- {
- child = new TMemberMapper();
- getList(this).Add(child);
- }
- setupAction?.Invoke(child);
- return child;
- }
-
- public MemberConfiguration()
- {
- NameMapper = new ParentSourceToDestinationNameMapper();
- MemberMappers.Add(new DefaultMember { NameMapper = NameMapper });
- }
-
- public bool MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceType, Type destType, Type destMemberType, string nameToSearch, List resolvers, bool isReverseMap)
- {
- foreach (var memberMapper in MemberMappers)
- {
- if (memberMapper.MapDestinationPropertyToSource(options, sourceType, destType, destMemberType, nameToSearch, resolvers, this, isReverseMap))
- {
- return true;
- }
- }
- return false;
- }
- }
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class NameSplitMember : IChildMemberConfiguration
- {
- public INamingConvention SourceMemberNamingConvention { get; set; }
- public INamingConvention DestinationMemberNamingConvention { get; set; }
-
- public bool MapDestinationPropertyToSource(ProfileMap options, TypeDetails sourceType, Type destType, Type destMemberType, string nameToSearch, List resolvers, IMemberConfiguration parent, bool isReverseMap)
- {
- var destinationMemberNamingConvention = isReverseMap
- ? SourceMemberNamingConvention
- : DestinationMemberNamingConvention;
- var sourceMemberNamingConvention = isReverseMap
- ? DestinationMemberNamingConvention
- : SourceMemberNamingConvention;
-
- var matches = destinationMemberNamingConvention.SplittingExpression
- ?.Matches(nameToSearch)
- .Cast()
- .Select(m => sourceMemberNamingConvention.ReplaceValue(m))
- .ToArray()
- ?? Array.Empty();
-
- MemberInfo matchingMemberInfo = null;
- for (var i = 1; i <= matches.Length; i++)
- {
- var first = string.Join(
- sourceMemberNamingConvention.SeparatorCharacter,
- matches.Take(i).Select(SplitMembers));
- var second = string.Join(
- sourceMemberNamingConvention.SeparatorCharacter,
- matches.Skip(i).Select(SplitMembers));
-
- matchingMemberInfo = parent.NameMapper.GetMatchingMemberInfo(sourceType, destType, destMemberType, first);
-
- if (matchingMemberInfo != null)
- {
- resolvers.Add(matchingMemberInfo);
-
- var details = options.CreateTypeDetails(matchingMemberInfo.GetMemberType());
- var foundMatch = parent.MapDestinationPropertyToSource(options, details, destType, destMemberType, second, resolvers, isReverseMap);
-
- if (!foundMatch)
- resolvers.RemoveAt(resolvers.Count - 1);
- else
- break;
- }
- }
- return matchingMemberInfo != null;
- string SplitMembers(string value) => sourceMemberNamingConvention.SplittingExpression.Replace(value, sourceMemberNamingConvention.ReplaceValue);
- }
- }
-}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs b/src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs
index 5a9aeb6799..74df41c28e 100644
--- a/src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs
+++ b/src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs
@@ -1,87 +1,82 @@
-using AutoMapper.Execution;
-using AutoMapper.Internal;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+namespace AutoMapper.Configuration;
-namespace AutoMapper.Configuration
+public interface ICtorParamConfigurationExpression
{
- public interface ICtorParamConfigurationExpression
- {
- ///
- /// Specify the source member(s) to map from.
- ///
- /// Property name referencing the source member to map against. Or a dot separated member path.
- void MapFrom(string sourceMembersPath);
- }
- public interface ICtorParamConfigurationExpression : ICtorParamConfigurationExpression
- {
- ///
- /// Map constructor parameter from member expression
- ///
- /// Member type
- /// Member expression
- void MapFrom(Expression> sourceMember);
+ ///
+ /// Specify the source member(s) to map from.
+ ///
+ /// Property name referencing the source member to map against. Or a dot separated member path.
+ void MapFrom(string sourceMembersPath);
+}
+public interface ICtorParamConfigurationExpression : ICtorParamConfigurationExpression
+{
+ ///
+ /// Map constructor parameter from member expression
+ ///
+ /// Member type
+ /// Member expression
+ void MapFrom(Expression> sourceMember);
- ///
- /// Map constructor parameter from custom func that has access to
- ///
- /// Not used for LINQ projection (ProjectTo)
- /// Custom func
- void MapFrom(Func resolver);
- }
- public interface ICtorParameterConfiguration
+ ///
+ /// Map constructor parameter from custom func that has access to
+ ///
+ /// Not used for LINQ projection (ProjectTo)
+ /// Custom func
+ void MapFrom(Func resolver);
+}
+public interface ICtorParameterConfiguration
+{
+ string CtorParamName { get; }
+ void Configure(TypeMap typeMap);
+}
+[EditorBrowsable(EditorBrowsableState.Never)]
+public class CtorParamConfigurationExpression : ICtorParamConfigurationExpression, ICtorParameterConfiguration
+{
+ public string CtorParamName { get; }
+ public Type SourceType { get; }
+
+ private readonly List> _ctorParamActions = new List>();
+
+ public CtorParamConfigurationExpression(string ctorParamName, Type sourceType)
{
- string CtorParamName { get; }
- void Configure(TypeMap typeMap);
+ CtorParamName = ctorParamName;
+ SourceType = sourceType;
}
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class CtorParamConfigurationExpression : ICtorParamConfigurationExpression, ICtorParameterConfiguration
- {
- public string CtorParamName { get; }
- public Type SourceType { get; }
- private readonly List> _ctorParamActions = new List>();
+ public void MapFrom(Expression> sourceMember) =>
+ _ctorParamActions.Add(cpm => cpm.MapFrom(sourceMember));
- public CtorParamConfigurationExpression(string ctorParamName, Type sourceType)
- {
- CtorParamName = ctorParamName;
- SourceType = sourceType;
- }
+ public void MapFrom(Func resolver)
+ {
+ Expression> resolverExpression = (src, dest, destMember, ctxt) => resolver(src, ctxt);
+ _ctorParamActions.Add(cpm => cpm.SetResolver(new FuncResolver(resolverExpression)));
+ }
- public void MapFrom(Expression> sourceMember) =>
- _ctorParamActions.Add(cpm => cpm.SetResolver(sourceMember));
+ public void MapFrom(string sourceMembersPath)
+ {
+ var sourceMembers = ReflectionHelper.GetMemberPath(SourceType, sourceMembersPath);
+ _ctorParamActions.Add(cpm => cpm.MapFrom(sourceMembersPath, sourceMembers));
+ }
- public void MapFrom(Func resolver)
+ public void Configure(TypeMap typeMap)
+ {
+ var ctorMap = typeMap.ConstructorMap;
+ if (ctorMap == null)
{
- Expression> resolverExpression = (src, dest, destMember, ctxt) => resolver(src, ctxt);
- _ctorParamActions.Add(cpm => cpm.Resolver = new FuncResolver(resolverExpression));
+ throw new AutoMapperConfigurationException($"The type {typeMap.DestinationType.Name} does not have a constructor.\n{typeMap.DestinationType.FullName}");
}
-
- public void MapFrom(string sourceMembersPath)
+ var parameter = ctorMap[CtorParamName];
+ if (parameter == null)
{
- ReflectionHelper.GetMemberPath(SourceType, sourceMembersPath);
- _ctorParamActions.Add(cpm => cpm.MapFrom(sourceMembersPath));
+ throw new AutoMapperConfigurationException($"{typeMap.DestinationType.Name} does not have a matching constructor with a parameter named '{CtorParamName}'.\n{typeMap.DestinationType.FullName}.{CheckRecord(ctorMap.Ctor)}");
}
-
- public void Configure(TypeMap typeMap)
+ foreach (var action in _ctorParamActions)
{
- var ctorMap = typeMap.ConstructorMap;
- if (ctorMap == null)
- {
- throw new AutoMapperConfigurationException($"The type {typeMap.DestinationType.Name} does not have a constructor.\n{typeMap.DestinationType.FullName}");
- }
- var parameter = ctorMap[CtorParamName];
- if (parameter == null)
- {
- throw new AutoMapperConfigurationException($"{typeMap.DestinationType.Name} does not have a matching constructor with a parameter named '{CtorParamName}'.\n{typeMap.DestinationType.FullName}");
- }
- foreach (var action in _ctorParamActions)
- {
- action(parameter);
- }
+ action(parameter);
}
+ return;
+ static string CheckRecord(ConstructorInfo ctor) => ctor.IsFamily && ctor.Has() ?
+ " When mapping to records, consider excluding non-public constructors. See https://docs.automapper.org/en/latest/Construction.html." : null;
}
}
\ No newline at end of file
diff --git a/src/AutoMapper/Configuration/IMappingExpression.cs b/src/AutoMapper/Configuration/IMappingExpression.cs
index 56d05765ff..c21663d6eb 100644
--- a/src/AutoMapper/Configuration/IMappingExpression.cs
+++ b/src/AutoMapper/Configuration/IMappingExpression.cs
@@ -1,138 +1,132 @@
-using AutoMapper.Configuration;
-using System;
-using System.Linq.Expressions;
-
-namespace AutoMapper
+namespace AutoMapper;
+///
+/// Mapping configuration options for non-generic maps
+///
+public interface IMappingExpression : IMappingExpressionBase