From 1aaa504db52b8d73bd2309db5b042ef9a060f68d Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 29 Apr 2026 10:21:55 +0500 Subject: [PATCH 1/2] feat: #938 - Add DirectAssignmentForSameType setting parametr --- src/Mapster.Tests/WhenConfiguringMapping.cs | 26 +++++++++++++++-- src/Mapster/Adapters/BaseAdapter.cs | 13 +++++++-- src/Mapster/TypeAdapterSetter.cs | 8 ++++++ src/Mapster/TypeAdapterSettings.cs | 7 +++++ src/Mapster/Utils/ReflectionUtils.cs | 31 +++++++++++++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/src/Mapster.Tests/WhenConfiguringMapping.cs b/src/Mapster.Tests/WhenConfiguringMapping.cs index 809e6387..076e5679 100644 --- a/src/Mapster.Tests/WhenConfiguringMapping.cs +++ b/src/Mapster.Tests/WhenConfiguringMapping.cs @@ -142,9 +142,9 @@ public void NewInstanceConfigurationTest() obj.Name = "Tim"; obj.Child = new TestNewInstanceF() { Name = "Kıvanç" }; - TypeAdapterConfig + TypeAdapterConfig .NewConfig() - .ShallowCopyForSameType(true); + .ShallowCopyForSameType(true); var newObj2 = TypeAdapter.Adapt(obj); @@ -156,6 +156,28 @@ public void NewInstanceConfigurationTest() Assert.IsTrue(newObj2.Child.Name == "Antalya"); } + [TestMethod] + public void WhenDirectAssignmentForSameTypeConfigurate() + { + TestNewInstanceD obj = new TestNewInstanceD(); + obj.Name = "Tim"; + obj.Child = new TestNewInstanceF() { Name = "Kıvanç" }; + + var config = new TypeAdapterConfig(); + config.NewConfig() + .DirectAssignmentForSameType(true); + + var newObj2 = TypeAdapter.Adapt(obj, config); + + Assert.IsTrue(newObj2.Name == "Tim"); + Assert.IsTrue(obj.Child.Name == newObj2.Child.Name); + + obj.Child.Name = "Antalya"; + + Assert.IsTrue(newObj2.Child.Name == "Antalya"); + } + + #region Data private Source _source; diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index 4bdddd86..1a8e3fd4 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -495,10 +495,19 @@ internal Expression CreateAdaptExpression(Expression source, Type destinationTyp if (_source.Type == destinationType && arg.MapType == MapType.Projection) return _source; + TypeAdapterRule? rule; + var tuple = new TypeTuple(_source.Type, destinationType); + arg.Context.Config.RuleMap.TryGetValue(tuple, out rule); + //adapt(_source); var notUsingDestinationValue = mapping is not { UseDestinationValue: true }; - var exp = _source.Type == destinationType && arg.Settings.ShallowCopyForSameType == true && notUsingDestinationValue && - !arg.Context.Config.HasRuleFor(_source.Type, destinationType) + + if(_source.Type == destinationType && notUsingDestinationValue + && arg.IsDirectAssignmentForSameTypeEnable(rule, tuple) && arg.IsNotCustomConverterFactory(rule)) + return _source.To(destinationType); + + var exp = _source.Type == destinationType && arg.Settings.ShallowCopyForSameType == true && notUsingDestinationValue + && rule == null ? _source : CreateAdaptExpressionCore(_source, destinationType, arg, mapping, destination); diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index 834f4dbe..5dd0df11 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -108,6 +108,14 @@ public static TSetter ShallowCopyForSameType(this TSetter setter, bool return setter; } + public static TSetter DirectAssignmentForSameType(this TSetter setter, bool value) where TSetter : TypeAdapterSetter + { + setter.CheckCompiled(); + + setter.Settings.DirectAssignmentForSameType = value; + return setter; + } + public static TSetter EnumMappingStrategy(this TSetter setter, EnumMappingStrategy strategy) where TSetter : TypeAdapterSetter { setter.CheckCompiled(); diff --git a/src/Mapster/TypeAdapterSettings.cs b/src/Mapster/TypeAdapterSettings.cs index f94172a3..38e3a53c 100644 --- a/src/Mapster/TypeAdapterSettings.cs +++ b/src/Mapster/TypeAdapterSettings.cs @@ -39,11 +39,18 @@ public bool? PreserveReference get => Get(nameof(PreserveReference)); set => Set(nameof(PreserveReference), value); } + public bool? DirectAssignmentForSameType + { + get => Get(nameof(DirectAssignmentForSameType)); + set => Set(nameof(DirectAssignmentForSameType), value); + } + public bool? ShallowCopyForSameType { get => Get(nameof(ShallowCopyForSameType)); set => Set(nameof(ShallowCopyForSameType), value); } + public bool? IgnoreNullValues { get => Get(nameof(IgnoreNullValues)); diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index aa798fc1..d397cff7 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -466,5 +466,36 @@ public static bool IsNotSelfCreation(this Type type) return type.GetFieldsAndProperties().All(it => (it.SetterModifier & (AccessModifier.Public | AccessModifier.NonPublic)) == 0); } + + public static bool IsDirectAssignmentForSameTypeEnable(this CompileArgument arg, TypeAdapterRule? rule, TypeTuple tuple) + { + if (rule != null + && rule.Settings.DirectAssignmentForSameType.HasValue) + return rule.Settings.DirectAssignmentForSameType.GetValueOrDefault(); + + if (arg.Context.Config.Rules + .Where(x => x.Settings.DestinationType == tuple.Destination) + .Select(x => x.Settings) + .Any(x => x.DirectAssignmentForSameType.HasValue && x.DirectAssignmentForSameType.GetValueOrDefault())) + return true; + + if (arg.Context.Config.Default.Settings.DirectAssignmentForSameType.GetValueOrDefault()) + return true; + + return false; + } + + public static bool IsNotCustomConverterFactory(this CompileArgument arg, TypeAdapterRule? rule) + { + if(rule != null) + { + if(arg.MapType == MapType.Map && rule.Settings.ConverterFactory != null) + return false; + if (arg.MapType == MapType.MapToTarget && rule.Settings.ConverterToTargetFactory != null) + return false; + } + + return true; + } } } From 290417fb8cb370609518359ab5c478cba630a2c6 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 6 May 2026 09:49:16 +0500 Subject: [PATCH 2/2] fix: refactoring and fix DirectAssignmentForSameType implementation --- src/Mapster/Adapters/BaseAdapter.cs | 5 ----- src/Mapster/Adapters/ClassAdapter.cs | 13 +++++++++++++ src/Mapster/Utils/ReflectionUtils.cs | 18 ------------------ 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index 1a8e3fd4..31d7541c 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -501,11 +501,6 @@ internal Expression CreateAdaptExpression(Expression source, Type destinationTyp //adapt(_source); var notUsingDestinationValue = mapping is not { UseDestinationValue: true }; - - if(_source.Type == destinationType && notUsingDestinationValue - && arg.IsDirectAssignmentForSameTypeEnable(rule, tuple) && arg.IsNotCustomConverterFactory(rule)) - return _source.To(destinationType); - var exp = _source.Type == destinationType && arg.Settings.ShallowCopyForSameType == true && notUsingDestinationValue && rule == null ? _source diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index 69af2101..042c0270 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -291,5 +291,18 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre return Expression.MemberInit(newInstance, lines); } + + protected override Expression CreateExpressionBody(Expression source, Expression? destination, CompileArgument arg) + { + TypeAdapterRule? rule; + var tuple = new TypeTuple(source.Type, arg.DestinationType); + arg.Context.Config.RuleMap.TryGetValue(tuple, out rule); + + if (source.Type == arg.DestinationType && !arg.UseDestinationValue + && arg.Settings.DirectAssignmentForSameType.GetValueOrDefault() && arg.IsNotCustomConverterFactory(rule)) + return source; + + return base.CreateExpressionBody(source, destination, arg); + } } } diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index d397cff7..81c3e21e 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -467,24 +467,6 @@ public static bool IsNotSelfCreation(this Type type) return type.GetFieldsAndProperties().All(it => (it.SetterModifier & (AccessModifier.Public | AccessModifier.NonPublic)) == 0); } - public static bool IsDirectAssignmentForSameTypeEnable(this CompileArgument arg, TypeAdapterRule? rule, TypeTuple tuple) - { - if (rule != null - && rule.Settings.DirectAssignmentForSameType.HasValue) - return rule.Settings.DirectAssignmentForSameType.GetValueOrDefault(); - - if (arg.Context.Config.Rules - .Where(x => x.Settings.DestinationType == tuple.Destination) - .Select(x => x.Settings) - .Any(x => x.DirectAssignmentForSameType.HasValue && x.DirectAssignmentForSameType.GetValueOrDefault())) - return true; - - if (arg.Context.Config.Default.Settings.DirectAssignmentForSameType.GetValueOrDefault()) - return true; - - return false; - } - public static bool IsNotCustomConverterFactory(this CompileArgument arg, TypeAdapterRule? rule) { if(rule != null)