diff --git a/src/AutoMapper/Execution/ExpressionBuilder.cs b/src/AutoMapper/Execution/ExpressionBuilder.cs index 3b3097156..b22b56fb4 100644 --- a/src/AutoMapper/Execution/ExpressionBuilder.cs +++ b/src/AutoMapper/Execution/ExpressionBuilder.cs @@ -168,7 +168,8 @@ Expression DefaultDestination() public static Expression ContextMap(TypePair typePair, Expression sourceParameter, Expression destinationParameter, MemberMap memberMap) { var mapMethod = ContextMapMethod.MakeGenericMethod(typePair.SourceType, typePair.DestinationType); - return Expression.Call(ContextParameter, mapMethod, sourceParameter, destinationParameter, Constant(memberMap, typeof(MemberMap))); + var source = ToType(sourceParameter, typePair.SourceType); + return Expression.Call(ContextParameter, mapMethod, source, destinationParameter, Constant(memberMap, typeof(MemberMap))); } public static Expression CheckContext(TypeMap typeMap) => typeMap.PreserveReferences || typeMap.MaxDepth > 0 ? CheckContextCall : null; public static Expression OverMaxDepth(TypeMap typeMap) => typeMap?.MaxDepth > 0 ? diff --git a/src/UnitTests/Bug/MapAtRuntime/MapAtRuntimeWithExtensionMethodAndNullable.cs b/src/UnitTests/Bug/MapAtRuntime/MapAtRuntimeWithExtensionMethodAndNullable.cs new file mode 100644 index 000000000..f3cfae151 --- /dev/null +++ b/src/UnitTests/Bug/MapAtRuntime/MapAtRuntimeWithExtensionMethodAndNullable.cs @@ -0,0 +1,48 @@ +namespace AutoMapper.UnitTests.Bug; + +public class MapAtRuntimeWithExtensionMethodAndNullable +{ + class SourceObject { public string MyValue { get; set; } } + class DestObject { public int? MyValue { get; set; } } + + [Fact] + public void Should_handle_extension_method_with_nullable_destination() + { + var mapper = new MapperConfiguration(cfg => + { + cfg.CreateMap() + .ForMember(dest => dest.MyValue, opt => + { + opt.MapFrom(src => src.MyValue.ExtractInt()); + opt.MapAtRuntime(); + }); + }).CreateMapper(); + + var result = mapper.Map(new SourceObject { MyValue = "1" }); + + result.MyValue.ShouldBe(1); + } + + [Fact] + public void Should_handle_null_source_member_with_extension_method_and_nullable_destination() + { + var mapper = new MapperConfiguration(cfg => + { + cfg.CreateMap() + .ForMember(dest => dest.MyValue, opt => + { + opt.MapFrom(src => src.MyValue.ExtractInt()); + opt.MapAtRuntime(); + }); + }).CreateMapper(); + + var result = mapper.Map(new SourceObject { MyValue = null }); + + result.MyValue.ShouldBeNull(); + } +} + +static class NullableExtensionMethodHelpers +{ + public static int? ExtractInt(this string str) => str == null ? null : int.Parse(str); +}