diff --git a/src/AutoMapper/Execution/ExpressionBuilder.cs b/src/AutoMapper/Execution/ExpressionBuilder.cs index 224a93db3d..1e875cb192 100644 --- a/src/AutoMapper/Execution/ExpressionBuilder.cs +++ b/src/AutoMapper/Execution/ExpressionBuilder.cs @@ -11,6 +11,7 @@ public static class ExpressionBuilder public static readonly Expression True = Constant(true, typeof(bool)); public static readonly Expression Null = Constant(null, typeof(object)); public static readonly Expression Empty = Empty(); + public static readonly LambdaExpression EmptyLambda = Expression.Lambda(Empty); public static readonly Expression Zero = Constant(0, typeof(int)); public static readonly ParameterExpression ExceptionParameter = Parameter(typeof(Exception), "ex"); public static readonly ParameterExpression ContextParameter = Parameter(typeof(ResolutionContext), "context"); @@ -74,7 +75,8 @@ public static Expression MapExpression(this IGlobalConfiguration configuration, if (typeMap != null) { var allowNull = memberMap?.AllowNull; - nullCheck = !typeMap.HasTypeConverter && allowNull.HasValue && allowNull != profileMap.AllowNullDestinationValues; + nullCheck = !typeMap.HasTypeConverter && (destination.NodeType != ExpressionType.Default || + (allowNull.HasValue && allowNull != profileMap.AllowNullDestinationValues)); if (!typeMap.HasDerivedTypesToInclude) { typeMap.Seal(configuration); diff --git a/src/AutoMapper/Execution/TypeMapPlanBuilder.cs b/src/AutoMapper/Execution/TypeMapPlanBuilder.cs index af1a0efac7..1e06ded536 100644 --- a/src/AutoMapper/Execution/TypeMapPlanBuilder.cs +++ b/src/AutoMapper/Execution/TypeMapPlanBuilder.cs @@ -288,7 +288,7 @@ private Expression CreateConstructorParameterExpression(ConstructorParameterMap var customSource = GetCustomSource(ctorParamMap); var resolvedExpression = BuildValueResolverFunc(ctorParamMap, customSource, defaultValue); var resolvedValue = Variable(resolvedExpression.Type, "resolvedValue"); - var mapMember = MapMember(ctorParamMap, defaultValue, resolvedValue); + var mapMember = MapMember(ctorParamMap, resolvedValue, defaultValue); _variables.Clear(); _variables.Add(resolvedValue); _expressions.Clear(); @@ -328,7 +328,7 @@ private Expression CreatePropertyMapFunc(MemberMap memberMap, Expression destina var valueResolver = BuildValueResolverFunc(memberMap, customSource, destinationMemberGetter); var resolvedValueVariable = Variable(valueResolver.Type, "resolvedValue"); var destinationMemberValue = DestinationMemberValue(memberMap, destinationMemberGetter, destinationMemberReadOnly); - var mappedMember = MapMember(memberMap, destinationMemberValue, resolvedValueVariable); + var mappedMember = MapMember(memberMap, resolvedValueVariable, destinationMemberValue); var mappedMemberVariable = SetVariables(valueResolver, resolvedValueVariable, mappedMember); var mapperExpr = destinationMemberReadOnly ? (Expression)mappedMemberVariable : Assign(destinationMemberAccess, mappedMemberVariable); if (memberMap.Condition != null) @@ -382,7 +382,7 @@ ParameterExpression SetVariables(Expression valueResolver, ParameterExpression r } return mappedMemberVariable; } - Expression MapMember(MemberMap memberMap, Expression destinationMemberValue, ParameterExpression resolvedValue) + Expression MapMember(MemberMap memberMap, ParameterExpression resolvedValue, Expression destinationMemberValue) { var typePair = memberMap.Types(); var profile = _typeMap.Profile; diff --git a/src/AutoMapper/TypeMap.cs b/src/AutoMapper/TypeMap.cs index fc5d2dcc4e..516022aac5 100644 --- a/src/AutoMapper/TypeMap.cs +++ b/src/AutoMapper/TypeMap.cs @@ -200,10 +200,7 @@ public void Seal(IGlobalConfiguration configuration) } _sealed = true; _details?.Seal(configuration, this); - if (!Projection) - { - MapExpression = CreateMapperLambda(configuration); - } + MapExpression = Projection ? EmptyLambda : CreateMapperLambda(configuration); SourceTypeDetails = null; DestinationTypeDetails = null; } diff --git a/src/UnitTests/NullBehavior.cs b/src/UnitTests/NullBehavior.cs index eeb0a1bad1..733e0739e8 100644 --- a/src/UnitTests/NullBehavior.cs +++ b/src/UnitTests/NullBehavior.cs @@ -1,5 +1,34 @@ namespace AutoMapper.UnitTests.NullBehavior; - +public class NullToExistingValue : AutoMapperSpecBase +{ + private record Person + { + public string Name { get; set; } + public Address TheAddress { get; set; } = new(); + } + private record Address + { + public string Street { get; set; } + public int Number { get; set; } + } + private record PersonModel + { + public string Name { get; set; } + public AddressModel TheAddress { get; set; } + } + private record AddressModel + { + public string Street { get; set; } + public int Number { get; set; } + } + protected override MapperConfiguration CreateConfiguration() => new(c => + { + c.CreateMap(); + c.CreateMap(); + }); + [Fact] + public void Should_overwrite() => Mapper.Map(new PersonModel(), new Person()).TheAddress.ShouldBeNull(); +} public class NullCheckDefault : AutoMapperSpecBase { class Source