diff --git a/src/Mapster.Tests/WhenPropertyNullablePropagationRegression.cs b/src/Mapster.Tests/WhenPropertyNullablePropagationRegression.cs
index d2239a18..1e1e27d9 100644
--- a/src/Mapster.Tests/WhenPropertyNullablePropagationRegression.cs
+++ b/src/Mapster.Tests/WhenPropertyNullablePropagationRegression.cs
@@ -12,12 +12,16 @@ public class WhenPropertyNullablePropagationRegression
///
///
[TestMethod]
- public async Task NotNullableStructMapToNotNullableCorrect()
+ public void NotNullableStructMapToNotNullableCorrect()
{
- TypeAdapterConfig
+ TypeAdapterConfig
+ .NewConfig()
+ .Map(dest => dest.Amount, src => src.Amount)
+ .Map(dest => dest.InnerAmount, src => src.Inner.Amount);
+
+ TypeAdapterConfig
.NewConfig()
- .Map(dest => dest.Amount, src => src.Amount)
- .Map(dest => dest.InnerAmount, src => src.Inner.Amount);
+ .IgnoreNullValues(true);
Foo858 foo = new()
@@ -32,12 +36,13 @@ public async Task NotNullableStructMapToNotNullableCorrect()
// Act
var bar = foo.Adapt();
+
// Assert
bar.InnerAmount.Amount.ShouldBe(10m);
}
[TestMethod]
- public async Task NotNullableStructMapToNullableCorrect()
+ public void NotNullableStructMapToNullableCorrect()
{
TypeAdapterConfig
.NewConfig()
@@ -61,6 +66,74 @@ public async Task NotNullableStructMapToNullableCorrect()
bar.InnerAmount?.Amount.ShouldBe(10m);
}
+ [TestMethod]
+ public void IgnoreNullValueWorkCorrect()
+ {
+ TypeAdapterConfig
+ .NewConfig()
+ .IgnoreNullValues(true)
+ .Map(dest => dest.Amount, src => src.Amount)
+ .Map(dest => dest.InnerAmount, src => src.Inner.Amount);
+
+ Foo858 foo = new()
+ {
+ Amount = new(1, Currency858.Usd),
+ Inner = new()
+ {
+ Amount = new(10, Currency858.Eur),
+ Int = 100,
+ }
+ };
+
+ var nullFoo = new Foo858() { Amount = new(2, Currency858.Ron), Inner = null };
+
+ // Act
+ var bar = foo.Adapt();
+ nullFoo.Adapt(bar);
+
+ // Assert
+ bar.InnerAmount.Amount.ShouldBe(10m);
+ bar.Amount.Amount.ShouldBe(2m);
+ bar.Amount.Currency.ShouldBe(Currency858.Ron);
+ }
+
+ [TestMethod]
+ public void MapToTargetWorkCorrect()
+ {
+ TypeAdapterConfig
+ .NewConfig()
+ .IgnoreNullValues(true)
+ .Map(dest => dest.Amount, src => src.Amount)
+ .Map(dest => dest.InnerAmount, src => src.Inner.Amount);
+
+ Foo858 foo = new()
+ {
+ Amount = new(1, Currency858.Usd),
+ Inner = new()
+ {
+ Amount = new(10, Currency858.Eur),
+ Int = 100,
+ }
+ };
+
+ var nullFoo = new Foo858() { Amount = new(2, Currency858.Ron), Inner = new()
+ {
+ Amount = new(20, Currency858.Eur),
+ Int = 100,
+ }
+ };
+
+ // Act
+ var bar = foo.Adapt();
+ nullFoo.Adapt(bar);
+
+ // Assert
+ bar.InnerAmount.Amount.ShouldBe(20m);
+ bar.Amount.Amount.ShouldBe(2m);
+ bar.Amount.Currency.ShouldBe(Currency858.Ron);
+ }
+
+
}
#region TestClasses
diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs
index 22314a5b..a8b7a4c0 100644
--- a/src/Mapster/Adapters/BaseClassAdapter.cs
+++ b/src/Mapster/Adapters/BaseClassAdapter.cs
@@ -132,7 +132,7 @@ select fn(src, destinationMember, arg))
{
propertyModel.Getter = arg.MapType == MapType.Projection
? getter
- : getter.ApplyPropertyNullPropagation(propertyModel);
+ : getter.ApplyPropertyNullPropagation();
properties.Add(propertyModel);
}
else
diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs
index 78cfacc1..a17b079a 100644
--- a/src/Mapster/Adapters/ClassAdapter.cs
+++ b/src/Mapster/Adapters/ClassAdapter.cs
@@ -114,7 +114,17 @@ protected override Expression CreateBlockExpression(Expression source, Expressio
? member.DestinationMember.GetExpression(destination)
: null;
- var adapt = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member, destMember);
+ 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);
if (member.UseDestinationValue
&& member.DestinationMember.Type.IsMapsterImmutable()
@@ -251,7 +261,17 @@ private static Expression SetValueByReflection(MemberMapping member, MemberExpre
if (member.DestinationMember.SetterModifier == AccessModifier.None)
continue;
- var value = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg, member);
+ 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);
//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 c00c5391..4e6af8a7 100644
--- a/src/Mapster/Utils/ExpressionEx.cs
+++ b/src/Mapster/Utils/ExpressionEx.cs
@@ -407,7 +407,7 @@ public static Expression NullableEnumExtractor(this Expression param)
return param;
}
- public static Expression ApplyPropertyNullPropagation(this Expression getter, MemberMapping property)
+ public static Expression ApplyPropertyNullPropagation(this Expression getter)
{
var current = getter;
var result = getter;
@@ -421,7 +421,7 @@ public static Expression ApplyPropertyNullPropagation(this Expression getter, Me
break;
if (expr.NodeType == ExpressionType.Parameter && condition != null)
{
- if (property.DestinationMember.Type.CanBeNull() && !getter.CanBeNull())
+ if (!getter.CanBeNull())
{
var transform = Expression.Convert(getter, typeof(Nullable<>).MakeGenericType(getter.Type));
return Expression.Condition(condition, transform, transform.Type.CreateDefault());
@@ -532,5 +532,23 @@ internal static Expression GetNameConverterExpression(Func conve
return Expression.Constant(converter);
}
+ public static bool IsNotPrimitiveNullableType(this Type type)
+ {
+ return Nullable.GetUnderlyingType(type) != null && !type.IsMapsterPrimitive();
+ }
+
+ public static Expression GetNotPrimitiveNullableValue(this Expression exp)
+ {
+ if (exp.Type.IsNotPrimitiveNullableType())
+ {
+ var getValueOrDefaultMethod = exp.Type.GetMethod("GetValueOrDefault", Type.EmptyTypes);
+ var getValue = Expression.Call(exp, getValueOrDefaultMethod);
+
+ return getValue;
+ }
+
+ return exp;
+ }
+
}
}