From ce8015fb831ddf14b017910a71261cd9e31490f5 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 18 May 2026 11:29:01 +0500 Subject: [PATCH 1/5] fix(docs): Add a description of the settings validation feature for open generic types --- docs/articles/configuration/Configuration.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/articles/configuration/Configuration.md b/docs/articles/configuration/Configuration.md index 34a30549..4254a951 100644 --- a/docs/articles/configuration/Configuration.md +++ b/docs/articles/configuration/Configuration.md @@ -70,7 +70,8 @@ TypeAdapterConfig.GlobalSettings.ForDestinationType() .AfterMapping(dest => dest.Validate()); ``` -NOTE: `ForDestinationType` above will always apply to all types assignable to `IValidator`. If destination class implements `IValidator`, it will also apply the `AfterMapping` config. +>[!NOTE] +>`ForDestinationType` above will always apply to all types assignable to `IValidator`. If destination class implements `IValidator`, it will also apply the `AfterMapping` config. ## Open generics @@ -80,3 +81,14 @@ If the mapping type is generic, you can create a setting by passing generic type TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>)) .Map("value", "Value"); ``` +>[!Note] +> Starting with Mapster version 10.0.8, [validation](xref:Mapster.Configuration.ValidationAndCompilation#validating-mappings) for type pairs settings containing open generic type is disabled. +>If you require validation of such settings, you must register the configuration for at least one pair of closed generic types: +>```csharp +>TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; // check all destination member mapping +>TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>)) +> .Map("value", "Value"); +> +> TypeAdapterConfig, GenericDto>.NewConfig(); +> TypeAdapterConfig.GlobalSettings.Compile(); // validate +>``` \ No newline at end of file From 91c9bd6572f8fba619d3353d720c2aaf0455766d Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 18 May 2026 15:42:12 +0500 Subject: [PATCH 2/5] feat(docs): Fix description for ShallowCopyForSameType setting Add description for DirectAssignmentForSameType setting --- docs/articles/settings/Shallow-merge.md | 52 +++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/docs/articles/settings/Shallow-merge.md b/docs/articles/settings/Shallow-merge.md index ba101264..05dce624 100644 --- a/docs/articles/settings/Shallow-merge.md +++ b/docs/articles/settings/Shallow-merge.md @@ -3,16 +3,62 @@ uid: Mapster.Settings.ShallowMerge title: "Settings - Shallow merge" --- -## Deep copy vs. shallow copy +## Deep copy vs. Shallow copy vs. Direct assignment -By default, Mapster will recursively map nested objects (deep copy). You can do shallow copying by setting `ShallowCopyForSameType` to `true`. +Depending on the settings specified, the mapping behavior may differ. +```csharp +var src = new Source { AProperty A {get; set;} , BProperty B {get; set;} } +var dest = src.Adapt(); +``` + +### Deep copy ```csharp -TypeAdapterConfig +// Deep copy - default behavior + var dest = new Destination { A = new AProperty{ int X = src.A.X }, B = new BProperty { int X = src.B.X} } +``` + +### Shallow copy + +>[!NOTE] +> 1. Changes mapping behavior only for member(property,field) source and destination type in registered custom mapping configuration. +> 2. Disable when destination member(property,field) using [`UseDestinationValue`](xref:Mapster.Settings.Custom.ReadonlyProperty) setting. +> 3. Disable when registered custom mapping configuration `.NewConfig()` for Type member(property,field). + +```csharp +//Shallow copy +TypeAdapterConfig .NewConfig() .ShallowCopyForSameType(true); + + var dest = new Destination { A = src.A, B = src.B } ``` +### Direct assignment [v10.0.8+] + +>[!NOTE] +> 1. Changes mapping behavior for Type. +> 2. Disable when destination member(property,field) using [`UseDestinationValue`](xref:Mapster.Settings.Custom.ReadonlyProperty) setting. +> 3. Disable when registered custom mapping configuration `.NewConfig()` contains the [`.MapWith()` and/or `MapToTargetWith()`](xref:Mapster.Settings.CustomConversionLogic) setting. + +```csharp +// DirectAssignment +TypeAdapterConfig + .NewConfig() + .DirectAssignmentForSameType(true); + + var dest = new Destination { A = src.A, B = new BProperty { int X = src.B.X} } + + //for mapping AProperty to AProperty + var a = new AProperty(); + var result = a.Adapt(); + + // behavior equal + var result = a; +``` + + + ## Copy vs. Merge By default, Mapster will map all properties, even source properties containing null values. You can copy only properties that have values (merge) by using `IgnoreNullValues` method. From 72e44e42110f0c8de16277297ff2256415958ff8 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 18 May 2026 16:04:19 +0500 Subject: [PATCH 3/5] fix(docs): update UseDestinationValue setting description --- .../settings/custom/Mapping-readonly-prop.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/articles/settings/custom/Mapping-readonly-prop.md b/docs/articles/settings/custom/Mapping-readonly-prop.md index c027b911..9a442ded 100644 --- a/docs/articles/settings/custom/Mapping-readonly-prop.md +++ b/docs/articles/settings/custom/Mapping-readonly-prop.md @@ -14,7 +14,7 @@ public class Order { } ``` -## Using `UseDestinationValue` attribute +### Using `UseDestinationValue` attribute You can make your type pure readonly and annotate with `[UseDestinationValue]`. @@ -27,7 +27,7 @@ public class Order { } ``` -## Convention based setup using `UseDestinationValue` Extension Method +### Convention based setup using `UseDestinationValue` Extension Method Or you can apply without annotate each type, for example, if you would like all readonly `ICollection<>` to use destination value. @@ -37,3 +37,11 @@ TypeAdapterConfig.GlobalSettings.Default member.Type.IsGenericType && member.Type.GetGenericTypeDefinition() == typeof(ICollection<>)); ``` + +### Using `UseDestinationValue` [v10.0.4+] + +```csharp +TypeAdapterConfig.GlobalSettings + .ForDestinationType() + .UseDestinationValue(dest=>dest.Items); +``` \ No newline at end of file From a44124a7a25ce00e6fbd34fbfc9f1a2990492f79 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 21 May 2026 11:55:20 +0500 Subject: [PATCH 4/5] feat(config): add Ignored Remove setters --- src/Mapster/TypeAdapterSetter.cs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index 5dd0df11..b50933de 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -73,6 +73,25 @@ public static TSetter IgnoreAttribute(this TSetter setter, params Type[ return setter; } + public static TSetter IgnoredClear(this TSetter setter) where TSetter : TypeAdapterSetter + { + setter.CheckCompiled(); + setter.Settings.Ignore.Clear(); + + return setter; + } + + public static TSetter IgnoredRemove(this TSetter setter, params string[] names) where TSetter : TypeAdapterSetter + { + setter.CheckCompiled(); + + foreach (var name in names) + { + setter.Settings.Ignore.TryRemove(name, out _); + } + return setter; + } + public static TSetter IncludeAttribute(this TSetter setter, params Type[] types) where TSetter : TypeAdapterSetter { setter.CheckCompiled(); @@ -554,6 +573,16 @@ internal TypeAdapterSetter(TypeAdapterSettings settings, TypeAdapterConfig paren return (TypeAdapterSetter)base.Ignore(members); } + public TypeAdapterSetter IgnoredRemove(params Expression>[] members) + { + this.CheckCompiled(); + + foreach (var member in members) + { + Settings.Ignore.TryRemove(member.GetMemberPath()!, out _); + } + return this; + } public new TypeAdapterSetter Map( Expression> member, Expression> source) From 609e5eda7677a1614fcb49cc0b60819f0ef6d2fa Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 21 May 2026 12:27:32 +0500 Subject: [PATCH 5/5] feat(test): add RemoveIgnoredFeatureIsWorked test --- src/Mapster.Tests/WhenIgnoreMapping.cs | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/Mapster.Tests/WhenIgnoreMapping.cs b/src/Mapster.Tests/WhenIgnoreMapping.cs index 9bed5dc3..107405df 100644 --- a/src/Mapster.Tests/WhenIgnoreMapping.cs +++ b/src/Mapster.Tests/WhenIgnoreMapping.cs @@ -111,8 +111,78 @@ public void MappingToIntefaceWithIgnorePrivateSetProperty() } + /// + /// https://github.com/MapsterMapper/Mapster/issues/953 + /// + [TestMethod] + public void RemoveIgnoredFeatureIsWorked() + { + TypeAdapterConfig + .NewConfig() + .Map(dest => dest.PocoName, src => src.DtoName) + .Ignore(x=>x.PocoName); + + TypeAdapterConfig + .NewConfig() + .Inherits(); + + var srcDerived = new DerivedPoco953 { PocoName = "Alice" }; + + // standart cases + var resultDerived = srcDerived.Adapt(); + + // when igrored member is remove + + var resultDerivedRemoveAllIgnored = srcDerived + .Adapt(cfg => + { + cfg + .NewConfig() + .Inherits() + .IgnoredClear(); + }); + + var resultDerivedRemove = srcDerived + .Adapt(cfg => + { + cfg + .NewConfig() + .Inherits() + .IgnoredRemove(x=>x.PocoName); + }); + + var resultDerivedRemoveByName = srcDerived + .Adapt(cfg => + { + cfg + .NewConfig() + .Inherits() + .IgnoredRemove("PocoName"); + }); + + + resultDerived.PocoName.ShouldNotBeNullOrEmpty(); + resultDerivedRemoveAllIgnored.PocoName.ShouldBe("Alice"); + resultDerivedRemove.PocoName.ShouldBe("Alice"); + resultDerivedRemoveByName.PocoName.ShouldBe("Alice"); + + } + #region TestClasses + public class BaseDTO953 + { + public string DtoName { get; set; } + } + public class DerivedDTO953: BaseDTO953 { } + + public class BasePoco953 + { + public string PocoName { get; set; } + } + + public class DerivedPoco953 : BasePoco953 { } + public interface InterfaceDestination723 { public string Inter { get; set; }