diff --git a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/INewInstanceBuilderContext.cs b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/INewInstanceBuilderContext.cs index 0ee8607a6e..004267af2f 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/INewInstanceBuilderContext.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/INewInstanceBuilderContext.cs @@ -13,4 +13,11 @@ public interface INewInstanceBuilderContext : IMembersBuilderContext void AddConstructorParameterMapping(ConstructorParameterMapping mapping); void AddInitMemberMapping(MemberAssignmentMapping mapping); + + /// + /// Maps case insensitive target root member names to their real case sensitive names. + /// For example id => Id. The real name can then be used as key for . + /// This allows resolving case insensitive configuration member names (eg. when mapping to constructor parameters). + /// + IReadOnlyDictionary RootTargetNameCasingMapping { get; } } diff --git a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/NewInstanceBuilderContext.cs b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/NewInstanceBuilderContext.cs index cf34999962..1bc87fe9dd 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/NewInstanceBuilderContext.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/NewInstanceBuilderContext.cs @@ -7,11 +7,17 @@ namespace Riok.Mapperly.Descriptors.MappingBodyBuilders.BuilderContext; /// An implementation of . /// /// The type of the mapping. -public class NewInstanceBuilderContext(MappingBuilderContext builderContext, T mapping) - : MembersMappingBuilderContext(builderContext, mapping), - INewInstanceBuilderContext +public class NewInstanceBuilderContext : MembersMappingBuilderContext, INewInstanceBuilderContext where T : INewInstanceObjectMemberMapping { + public IReadOnlyDictionary RootTargetNameCasingMapping { get; } + + public NewInstanceBuilderContext(MappingBuilderContext builderContext, T mapping) + : base(builderContext, mapping) + { + RootTargetNameCasingMapping = MemberConfigsByRootTargetName.ToDictionary(x => x.Key, x => x.Key, StringComparer.OrdinalIgnoreCase); + } + public void AddInitMemberMapping(MemberAssignmentMapping mapping) { SetSourceMemberMapped(mapping.SourcePath); @@ -20,7 +26,8 @@ public void AddInitMemberMapping(MemberAssignmentMapping mapping) public void AddConstructorParameterMapping(ConstructorParameterMapping mapping) { - MemberConfigsByRootTargetName.Remove(mapping.Parameter.Name); + var paramName = RootTargetNameCasingMapping.GetValueOrDefault(mapping.Parameter.Name, defaultValue: mapping.Parameter.Name); + MemberConfigsByRootTargetName.Remove(paramName); SetSourceMemberMapped(mapping.DelegateMapping.SourcePath); Mapping.AddConstructorParameterMapping(mapping); } diff --git a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/NewInstanceContainerBuilderContext.cs b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/NewInstanceContainerBuilderContext.cs index 725b809782..c8623032aa 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/NewInstanceContainerBuilderContext.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/NewInstanceContainerBuilderContext.cs @@ -8,11 +8,17 @@ namespace Riok.Mapperly.Descriptors.MappingBodyBuilders.BuilderContext; /// which supports containers (). /// /// -public class NewInstanceContainerBuilderContext(MappingBuilderContext builderContext, T mapping) - : MembersContainerBuilderContext(builderContext, mapping), - INewInstanceBuilderContext +public class NewInstanceContainerBuilderContext : MembersContainerBuilderContext, INewInstanceBuilderContext where T : INewInstanceObjectMemberMapping, IMemberAssignmentTypeMapping { + public IReadOnlyDictionary RootTargetNameCasingMapping { get; } + + public NewInstanceContainerBuilderContext(MappingBuilderContext builderContext, T mapping) + : base(builderContext, mapping) + { + RootTargetNameCasingMapping = MemberConfigsByRootTargetName.ToDictionary(x => x.Key, x => x.Key, StringComparer.OrdinalIgnoreCase); + } + public void AddInitMemberMapping(MemberAssignmentMapping mapping) { SetSourceMemberMapped(mapping.SourcePath); @@ -21,7 +27,8 @@ public void AddInitMemberMapping(MemberAssignmentMapping mapping) public void AddConstructorParameterMapping(ConstructorParameterMapping mapping) { - MemberConfigsByRootTargetName.Remove(mapping.Parameter.Name); + var paramName = RootTargetNameCasingMapping.GetValueOrDefault(mapping.Parameter.Name, defaultValue: mapping.Parameter.Name); + MemberConfigsByRootTargetName.Remove(paramName); SetSourceMemberMapped(mapping.DelegateMapping.SourcePath); Mapping.AddConstructorParameterMapping(mapping); } diff --git a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/NewInstanceObjectMemberMappingBodyBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/NewInstanceObjectMemberMappingBodyBuilder.cs index e7693f947d..f6d4974012 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/NewInstanceObjectMemberMappingBodyBuilder.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/NewInstanceObjectMemberMappingBodyBuilder.cs @@ -320,7 +320,10 @@ out PropertyMappingConfiguration? memberConfig sourcePath = null; memberConfig = null; - if (!ctx.MemberConfigsByRootTargetName.TryGetValue(parameter.Name, out var memberConfigs)) + if ( + !ctx.RootTargetNameCasingMapping.TryGetValue(parameter.Name, out var parameterName) + || !ctx.MemberConfigsByRootTargetName.TryGetValue(parameterName, out var memberConfigs) + ) { return ctx.BuilderContext .SymbolAccessor diff --git a/test/Riok.Mapperly.Tests/Mapping/CtorTest.cs b/test/Riok.Mapperly.Tests/Mapping/CtorTest.cs index 1dc6e68696..0cda39539d 100644 --- a/test/Riok.Mapperly.Tests/Mapping/CtorTest.cs +++ b/test/Riok.Mapperly.Tests/Mapping/CtorTest.cs @@ -79,4 +79,33 @@ public void DeepCloneRecordShouldNotUseCtorMapping() """ ); } + + [Fact] + public void MapPropertyCtorMapping() + { + var source = TestSourceBuilder.MapperWithBodyAndTypes( + """ + [MapProperty(nameof(A.BId), nameof(B.Id))] + private partial B Map(A source); + """, + "class A { public long BId { get; } }", + """ + class B + { + public long Id { get; } + + public B(long id) { Id = id; } + } + """ + ); + TestHelper + .GenerateMapper(source) + .Should() + .HaveSingleMethodBody( + """ + var target = new global::B(source.BId); + return target; + """ + ); + } }