diff --git a/src/Mapster.Tests/WhenCtorNullableParamMapping.cs b/src/Mapster.Tests/WhenCtorNullableParamMapping.cs
index bef0b16f..e1884624 100644
--- a/src/Mapster.Tests/WhenCtorNullableParamMapping.cs
+++ b/src/Mapster.Tests/WhenCtorNullableParamMapping.cs
@@ -1,5 +1,6 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;
+using System.Collections.Generic;
namespace Mapster.Tests
{
@@ -60,6 +61,27 @@ public void Dto_To_Domain_AbstractClassNull_MapsCorrectly()
}
+ ///
+ /// https://github.com/MapsterMapper/Mapster/issues/943
+ ///
+ [TestMethod]
+ public void NullableCtorPropagationCurrentWorkWithDestinationTransform()
+ {
+ var config = new TypeAdapterConfig();
+
+ config.Default
+ .AddDestinationTransform(DestinationTransform.EmptyCollectionIfNull);
+
+ // Arrange
+ var fooDto = new FooDto943();
+
+ // Act
+ var foo = fooDto.Adapt(config);
+
+ // Assert
+ foo.Strings.ShouldNotBeNull();
+ }
+
#region Immutable classes with private setters, map via ctors
private abstract class AbstractDomainTestClass
{
@@ -96,6 +118,13 @@ public DomainTestClass(
#endregion
#region DTO classes
+
+ class FooDto943
+ {
+ public string[] Strings { get; set; }
+ }
+
+ record Foo943(List Strings);
private abstract class AbstractDtoTestClass
{
public string AbstractProperty { get; set; }
diff --git a/src/Mapster/Adapters/BaseAdapter.cs b/src/Mapster/Adapters/BaseAdapter.cs
index 31d7541c..b31a0dbe 100644
--- a/src/Mapster/Adapters/BaseAdapter.cs
+++ b/src/Mapster/Adapters/BaseAdapter.cs
@@ -448,7 +448,7 @@ protected virtual Expression CreateInstantiationExpression(Expression source, Ex
}
}
- private static Expression CreateAdaptExpressionCore(Expression source, Type destinationType, CompileArgument arg, MemberMapping? mapping = null, Expression? destination = null)
+ internal static Expression CreateAdaptExpressionCore(Expression source, Type destinationType, CompileArgument arg, MemberMapping? mapping = null, Expression? destination = null)
{
var mapType = arg.MapType == MapType.MapToTarget && destination == null ? MapType.Map :
mapping?.UseDestinationValue == true ? MapType.MapToTarget :
@@ -501,10 +501,24 @@ internal Expression CreateAdaptExpression(Expression source, Type destinationTyp
//adapt(_source);
var notUsingDestinationValue = mapping is not { UseDestinationValue: true };
- var exp = _source.Type == destinationType && arg.Settings.ShallowCopyForSameType == true && notUsingDestinationValue
- && rule == null
- ? _source
- : CreateAdaptExpressionCore(_source, destinationType, arg, mapping, destination);
+ Expression exp;
+
+ if (_source.Type == destinationType && arg.Settings.ShallowCopyForSameType == true
+ && notUsingDestinationValue && rule == null)
+ exp = _source;
+ else if (source is ConditionalExpression cond && mapping != null)
+ {
+ // convert ApplyNullable Propagation for NotPrimitive Nullable types
+ if (mapping.Getter.Type.IsNotPrimitiveNullableType() && !mapping.DestinationMember.Type.IsNullable())
+ {
+ var adapt = CreateAdaptExpressionCore(cond.IfTrue.GetNotPrimitiveNullableValue(), mapping.DestinationMember.Type, arg, mapping);
+ exp = Expression.Condition(cond.Test, adapt, mapping.DestinationMember.Type.CreateDefault());
+ }
+ else
+ exp = CreateAdaptExpressionCore(_source, destinationType, arg, mapping, destination);
+ }
+ else
+ exp = CreateAdaptExpressionCore(_source, destinationType, arg, mapping, destination);
//transform(adapt(_source));
if (notUsingDestinationValue)
diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs
index 1d6f6b64..d719409e 100644
--- a/src/Mapster/Adapters/BaseClassAdapter.cs
+++ b/src/Mapster/Adapters/BaseClassAdapter.cs
@@ -247,7 +247,7 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi
}
else
getter = member.Getter
- .ApplyNullPropagationFromCtor(CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member), arg);
+ .ApplyNullPropagationFromCtor(CreateAdaptExpressionCore(member.Getter, member.DestinationMember.Type, arg, member), arg);
if (member.Ignore.Condition != null)
diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs
index 042c0270..27d09d9b 100644
--- a/src/Mapster/Adapters/ClassAdapter.cs
+++ b/src/Mapster/Adapters/ClassAdapter.cs
@@ -114,17 +114,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio
? member.DestinationMember.GetExpression(destination)
: null;
- Expression adapt;
-
- // convert ApplyNullable Propagation for NotPrimitive Nullable types
- if (member.Getter is ConditionalExpression cond && member.Getter.Type.IsNotPrimitiveNullableType()
- && !member.DestinationMember.Type.IsNullable())
- {
- var value = CreateAdaptExpression(cond.IfTrue.GetNotPrimitiveNullableValue(), member.DestinationMember.Type, arg, member, destMember);
- adapt = Expression.Condition(cond.Test, value, member.DestinationMember.Type.CreateDefault());
- }
- else
- adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member, destMember);
+ var adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member, destMember);
if (member.UseDestinationValue
&& member.DestinationMember.Type.IsMapsterImmutable()
@@ -262,17 +252,7 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre
if (member.DestinationMember.SetterModifier == AccessModifier.None)
continue;
- Expression value;
-
- // convert ApplyNullable Propagation for NotPrimitive Nullable types
- if (member.Getter is ConditionalExpression cond && member.Getter.Type.IsNotPrimitiveNullableType()
- && !member.DestinationMember.Type.IsNullable())
- {
- var adapt = CreateAdaptExpression(cond.IfTrue.GetNotPrimitiveNullableValue(), member.DestinationMember.Type, arg, member);
- value = Expression.Condition(cond.Test, adapt, member.DestinationMember.Type.CreateDefault());
- }
- else
- value = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member);
+ var value = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member);
//special null property check for projection
//if we don't set null to property, EF will create empty object
diff --git a/src/Mapster/Utils/ExpressionEx.cs b/src/Mapster/Utils/ExpressionEx.cs
index 4e6af8a7..b7ffc365 100644
--- a/src/Mapster/Utils/ExpressionEx.cs
+++ b/src/Mapster/Utils/ExpressionEx.cs
@@ -483,7 +483,12 @@ public static Expression ApplyNullPropagationFromCtor(this Expression getter, Ex
if (condition == null)
return adapt;
- return Expression.Condition(condition, adapt, adapt.Type.CreateDefault());
+ // add supporting DestinationTransforms
+ var transform = arg.Settings.DestinationTransforms.Find(it => it.Condition(adapt.Type));
+ if (transform != null)
+ return transform.TransformFunc(adapt.Type).Apply(arg.MapType, Expression.Condition(condition, adapt, Expression.Default(adapt.Type)));
+
+ return Expression.Condition(condition, adapt, Expression.Default(adapt.Type));
}
public static string? GetMemberPath(this LambdaExpression lambda, bool firstLevelOnly = false, bool noError = false)