diff --git a/src/Mapster.Tests/WhenUsingMapWithOverrideTypesSettings.cs b/src/Mapster.Tests/WhenUsingMapWithOverrideTypesSettings.cs new file mode 100644 index 00000000..673e926b --- /dev/null +++ b/src/Mapster.Tests/WhenUsingMapWithOverrideTypesSettings.cs @@ -0,0 +1,89 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; +using System; +using System.Collections.Generic; + +namespace Mapster.Tests +{ + [TestClass] + public class WhenUsingMapWithOverrideTypesSettings + { + [TestMethod] + public void OverrideDestinationTramsformIsWorked() + { + var config = new TypeAdapterConfig(); + config.Default.AddDestinationTransform(DestinationTransform.EmptyCollectionIfNull); + + config + .NewConfig() + .MapWithTypeSettingsOverride(src => src.Children, dest => dest.Children, + cfg => + { + cfg.SkipSettings(x => x.DestinationTransforms); + }) + .MapWithTypeSettingsOverride(src => src.Array, dest => dest.Array, + cfg => + { + cfg + .ReConfigurate() + .MapWith(x => x ?? new[] { 42 }); + }); + + + var source = new CollectionPocoOverride(); + var destination = source.Adapt(config); + + destination.MultiDimentionalArray.Length.ShouldBe(0); + destination.ChildDict.Count.ShouldBe(0); + destination.Set.Count.ShouldBe(0); + + + destination.Children.ShouldBeNull(); // Destination Transforms from global context settings is skipped for this property + destination.Array[0].ShouldBe(42); // Custom converter for types is worked, Destination Transforms is not achievable because the custom converter never returns null + + + var destWithNotTypesSettingOverride = new CollectionPocoOverride().Adapt(config); + + // Destination Transforms correct work from other mapping types + destWithNotTypesSettingOverride.Array.Length.ShouldBe(0); + } + + + #region TestClasses + + class CollectionPocoWithArray + { + public int[] Array { get; set; } + } + + class CollectionDtoWithArray + { + public int[] Array { get; set; } + } + + class CollectionPocoOverride + { + public Guid Id { get; set; } + public string Name { get; set; } + + public List Children { get; set; } + public int[] Array { get; set; } + public double[,] MultiDimentionalArray { get; set; } + public Dictionary ChildDict { get; set; } + public HashSet Set { get; set; } + } + + class CollectionDtoOverride + { + public Guid Id { get; set; } + public string Name { get; set; } + + public IReadOnlyList Children { get; internal set; } + public int[] Array { get; set; } + public double[,] MultiDimentionalArray { get; set; } + public IReadOnlyDictionary ChildDict { get; set; } + public ISet Set { get; set; } + } + #endregion TestClasses + } +} diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs index b31a0dbe..930380b9 100644 --- a/src/Mapster/Adapters/BaseAdapter.cs +++ b/src/Mapster/Adapters/BaseAdapter.cs @@ -523,7 +523,9 @@ internal Expression CreateAdaptExpression(Expression source, Type destinationTyp //transform(adapt(_source)); if (notUsingDestinationValue) { - var transform = arg.Settings.DestinationTransforms.Find(it => it.Condition(exp.Type)); + var settings = mapping?.OverrideSettings ?? arg.Settings; + + var transform = settings.DestinationTransforms.Find(it => it.Condition(exp.Type)); if (transform != null) exp = transform.TransformFunc(exp.Type).Apply(arg.MapType, exp); } diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index 6d12a934..17a7994b 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -43,6 +43,12 @@ src is LambdaExpression lambda from src in sources select fn(src, destinationMember, arg)) .FirstOrDefault(result => result != null); + if(getter is MemberExpression mem && mem?.Expression?.Type == source.Type) + { + getter = Expression.PropertyOrField(source, mem.Member.Name); + } + + var test = resolvers.Where(ValueAccessingStrategy.CustomResolvers.Contains); if (arg.MapType == MapType.Projection && getter != null) { @@ -103,6 +109,10 @@ select fn(src, destinationMember, arg)) var nextResolvers = arg.Settings.Resolvers.Next(arg.Settings.Ignore, (ParameterExpression)source, destinationMember.Name) .ToList(); + var overideSettings = arg.Settings.Resolvers + .Where(x => x.DestinationMemberName == destinationMember.Name && x.OvverideSettings != null) + .Select(x=>x.OvverideSettings).FirstOrDefault(); + var propertyModel = new MemberMapping { DestinationMember = destinationMember, @@ -112,6 +122,7 @@ select fn(src, destinationMember, arg)) Source = (ParameterExpression)source, Destination = (ParameterExpression?)destination, UseDestinationValue = IsCanUsingDestinationValue(arg, destinationMember), + OverrideSettings = overideSettings }; if(arg.MapType == MapType.ApplyNullPropagation && getter == null && !arg.DestinationType.IsRecordType() @@ -264,6 +275,7 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi else getter = member.Getter .ApplyNullPropagationFromCtor(CreateAdaptExpressionCore(member.Getter, member.DestinationMember.Type, arg, member), arg); + if (member.Ignore.Condition != null) @@ -282,6 +294,7 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi if (arg.MapType == MapType.MapToTarget && arg.DestinationType.IsRecordType()) getter = TryRestoreRecordMember(member.DestinationMember, recordRestorParamModel, destination) ?? getter; } + } arguments.Add(getter); } diff --git a/src/Mapster/Models/InvokerModel.cs b/src/Mapster/Models/InvokerModel.cs index b5a4546d..f2201c3d 100644 --- a/src/Mapster/Models/InvokerModel.cs +++ b/src/Mapster/Models/InvokerModel.cs @@ -9,6 +9,7 @@ public class InvokerModel public LambdaExpression? Invoker { get; set; } public string? SourceMemberName { get; set; } public LambdaExpression? Condition { get; set; } + public TypeAdapterSettings? OvverideSettings { get; set; } public bool IsChildPath { get; set; } public InvokerModel? Next(ParameterExpression source, string destMemberName) diff --git a/src/Mapster/Models/MemberMapping.cs b/src/Mapster/Models/MemberMapping.cs index d047e4ba..008277d6 100644 --- a/src/Mapster/Models/MemberMapping.cs +++ b/src/Mapster/Models/MemberMapping.cs @@ -13,6 +13,7 @@ internal class MemberMapping public ParameterExpression Source; public ParameterExpression? Destination; public bool UseDestinationValue; + public TypeAdapterSettings? OverrideSettings; public bool HasSettings() { diff --git a/src/Mapster/Settings/SettingStore.cs b/src/Mapster/Settings/SettingStore.cs index 821b8569..fbf34b9b 100644 --- a/src/Mapster/Settings/SettingStore.cs +++ b/src/Mapster/Settings/SettingStore.cs @@ -1,6 +1,8 @@ using System; using System.Collections; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; namespace Mapster { @@ -46,19 +48,18 @@ public T Get(string key, Func initializer) where T : class return (T)_objectStore.GetOrAdd(key, _ => initializer()); } - public virtual void Apply(object other) - { - if (other is SettingStore settingStore) - Apply(settingStore); - } - public void Apply(SettingStore other) + + private void ApplyBoolSettings (IEnumerable> otherBoolStore) { - foreach (var kvp in other._booleanStore) + foreach (var kvp in otherBoolStore) { _booleanStore.TryAdd(kvp.Key, kvp.Value); } + } - foreach (var kvp in other._objectStore) + private void ApplyObjectSettings(IEnumerable> otherBoolStore) + { + foreach (var kvp in otherBoolStore) { var self = _objectStore.GetOrAdd(kvp.Key, key => { @@ -80,5 +81,26 @@ public void Apply(SettingStore other) } } } + + + public virtual void Apply(object other) + { + if (other is SettingStore settingStore) + Apply(settingStore); + } + + + public virtual void Apply(SettingStore other) + { + ApplyBoolSettings(other._booleanStore); + ApplyObjectSettings(other._objectStore); + } + + public virtual void ApplyWithSkipSettings(SettingStore other, List skipSettingNames) + { + ApplyBoolSettings(other._booleanStore.Where(x => !skipSettingNames.Contains(x.Key))); + ApplyObjectSettings(other._objectStore.Where(x => !skipSettingNames.Contains(x.Key))); + } + } } \ No newline at end of file diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index 1dce2053..9adf790c 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -474,6 +474,13 @@ internal LambdaExpression CreateInlineMapExpression(Type sourceType, Type destin arg.Settings.Resolvers.AddRange(mapping.NextResolvers); arg.Settings.Ignore.Apply(mapping.NextIgnore); arg.UseDestinationValue = mapping.UseDestinationValue; + + if (mapping.OverrideSettings != null) + { + mapping.OverrideSettings.Apply(arg.Settings); + arg.Settings = mapping.OverrideSettings; + } + } return CreateMapExpression(arg); diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index b50933de..1bbaea12 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -22,6 +22,16 @@ public TypeAdapterSetter(TypeAdapterSettings settings, TypeAdapterConfig config) Settings = settings; Config = config; } + + internal static TypeAdapterSetter CreateMapTypeOverride() + { + return new TypeAdapterSetter(new TypeAdapterSettings(), null); + } + + internal static TypeAdapterSetter CreateMapTypeOverride() + { + return new TypeAdapterSetter(new TypeAdapterSettings(), null); + } } public static class TypeAdapterSetterExtensions { @@ -629,6 +639,42 @@ public TypeAdapterSetter IgnoredRemove(params Expression MapWithTypeSettingsOverride( + Expression> member, + Expression> source, + Action>? configAction = null) + { + this.CheckCompiled(); + + var invoker = Expression.Lambda(source.Body, Expression.Parameter(typeof(object))); + if (member.IsIdentity()) + { + Settings.ExtraSources.Add(invoker); + return this; + } + + TypeAdapterSettings? overrideSettings = null; + + if (configAction != null) + { + var Tempsetter = new OverrideTypesSetter(); + configAction(Tempsetter); + + overrideSettings = Tempsetter.Settings; + } + + Settings.Resolvers.Add(new InvokerModel + { + DestinationMemberName = member.GetMemberPath()!, + Invoker = invoker, + Condition = null, + OvverideSettings = overrideSettings + }); + return this; + } + + + public TypeAdapterSetter IgnoreIf( Expression> condition, params Expression>[] members) diff --git a/src/Mapster/TypeAdapterSetters/OverrideTypesSetter.cs b/src/Mapster/TypeAdapterSetters/OverrideTypesSetter.cs new file mode 100644 index 00000000..6a02b059 --- /dev/null +++ b/src/Mapster/TypeAdapterSetters/OverrideTypesSetter.cs @@ -0,0 +1,35 @@ +using Mapster.Utils; +using System; +using System.Linq.Expressions; + +namespace Mapster +{ + [AdaptWith(AdaptDirectives.DestinationAsRecord)] + public class OverrideTypesSetter : TypeAdapterSetter + { + protected OverrideTypesSettings _Settings { get => (OverrideTypesSettings)Settings; } + + public OverrideTypesSetter() : this (new OverrideTypesSettings (), null) { } + public OverrideTypesSetter(TypeAdapterSettings settings, TypeAdapterConfig config) : base(settings, config) { } + } + + public class OverrideTypesSetter : OverrideTypesSetter + { + public OverrideTypesSetter SkipSettings(params Expression>[] settings) + { + foreach (var member in settings) + { + _Settings.DropSettings.Add(member.GetMemberPath()!); + } + + return this; + } + + public TypeAdapterSetter ReConfigurate() + { + return new TypeAdapterSetter(this.Settings,this.Config); + } + } + + +} diff --git a/src/Mapster/TypeAdapterSettings/OverrideTypesSettings.cs b/src/Mapster/TypeAdapterSettings/OverrideTypesSettings.cs new file mode 100644 index 00000000..614f5e94 --- /dev/null +++ b/src/Mapster/TypeAdapterSettings/OverrideTypesSettings.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace Mapster +{ + [AdaptWith(AdaptDirectives.DestinationAsRecord)] + public class OverrideTypesSettings : TypeAdapterSettings + { + public List DropSettings + { + get => Get(nameof(DropSettings), () => new List()); + } + + public override void Apply(object other) + { + if (other is SettingStore settingStore) + Apply(settingStore); + } + + public override void Apply(SettingStore other) + { + base.ApplyWithSkipSettings(other, DropSettings); + } + } +} diff --git a/src/Mapster/Utils/ExpressionEx.cs b/src/Mapster/Utils/ExpressionEx.cs index b7ffc365..1289edef 100644 --- a/src/Mapster/Utils/ExpressionEx.cs +++ b/src/Mapster/Utils/ExpressionEx.cs @@ -150,7 +150,7 @@ public static Expression Not(Expression exp) public static Expression Apply(this LambdaExpression lambda, MapType mapType, params Expression[] exps) { return lambda.Apply(mapType != MapType.Projection, exps); - } + } public static Expression Apply(this LambdaExpression lambda, ParameterExpression p1, ParameterExpression? p2 = null) {