From 0416767c391ae705166956347c3a9752aac977f0 Mon Sep 17 00:00:00 2001 From: wuyangfan Date: Mon, 25 May 2026 14:13:02 +0800 Subject: [PATCH] fix: use type default for optional DateTime constructor params (#954) When an optional constructor parameter has a null/DBNull default value, emit destinationType.CreateDefault() instead of Expression.Constant(null). Co-authored-by: Cursor --- ...ngRecordWithOptionalDateTimeConstructor.cs | 31 +++++++++++++++++++ src/Mapster/Adapters/BaseClassAdapter.cs | 27 +++++++--------- 2 files changed, 42 insertions(+), 16 deletions(-) create mode 100644 src/Mapster.Tests/WhenMappingRecordWithOptionalDateTimeConstructor.cs diff --git a/src/Mapster.Tests/WhenMappingRecordWithOptionalDateTimeConstructor.cs b/src/Mapster.Tests/WhenMappingRecordWithOptionalDateTimeConstructor.cs new file mode 100644 index 00000000..b772dbd7 --- /dev/null +++ b/src/Mapster.Tests/WhenMappingRecordWithOptionalDateTimeConstructor.cs @@ -0,0 +1,31 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; + +namespace Mapster.Tests +{ + [TestClass] + public class WhenMappingRecordWithOptionalDateTimeConstructor + { + [TestMethod] + public void Record_With_DateTime_Maps_To_Class_With_Optional_DateTime_Constructor() + { + var source = new DateTimeFoo(DateTime.Today); + var destination = source.Adapt(); + + destination.Timestamp.ShouldBe(source.Timestamp); + } + + public class DateTimeFooDto + { + public DateTime Timestamp { get; set; } + + public DateTimeFooDto(DateTime timestamp = default) + { + Timestamp = timestamp; + } + } + + public record DateTimeFoo(DateTime Timestamp); + } +} diff --git a/src/Mapster/Adapters/BaseClassAdapter.cs b/src/Mapster/Adapters/BaseClassAdapter.cs index 6d12a934..57687d02 100644 --- a/src/Mapster/Adapters/BaseClassAdapter.cs +++ b/src/Mapster/Adapters/BaseClassAdapter.cs @@ -225,23 +225,9 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi Expression defaultConst; Expression getter; -#if NETSTANDARD2_0 - try - { - defaultConst = parameterInfo.IsOptional && parameterInfo.DefaultValue != null - ? Expression.Constant(parameterInfo.DefaultValue, member.DestinationMember.Type) - : parameterInfo.ParameterType.CreateDefault(); - } - catch (FormatException) - { - defaultConst = parameterInfo.ParameterType.CreateDefault(); - } - -#else - defaultConst = parameterInfo.IsOptional && parameterInfo.DefaultValue != null - ? Expression.Constant(parameterInfo.DefaultValue, member.DestinationMember.Type) + defaultConst = parameterInfo.IsOptional + ? CreateOptionalParameterDefault(parameterInfo, member.DestinationMember.Type) : parameterInfo.ParameterType.CreateDefault(); -#endif if (member.Getter == null) { @@ -289,6 +275,15 @@ protected Expression CreateInstantiationExpression(Expression source, ClassMappi return Expression.New(classConverter.ConstructorInfo!, arguments); } + static Expression CreateOptionalParameterDefault(ParameterInfo parameterInfo, Type destinationType) + { + var defaultValue = parameterInfo.DefaultValue; + if (defaultValue == null || defaultValue is DBNull) + return destinationType.CreateDefault(); + + return Expression.Constant(defaultValue, destinationType); + } + protected virtual ClassModel GetConstructorModel(ConstructorInfo ctor, bool breakOnUnmatched) { return new ClassModel