diff --git a/Dapper/SqlMapper.cs b/Dapper/SqlMapper.cs index 8f3956cab..cec003f32 100644 --- a/Dapper/SqlMapper.cs +++ b/Dapper/SqlMapper.cs @@ -2817,7 +2817,14 @@ private static bool IsValueTuple(Type? type) => (type?.IsValueType == true il.Emit(OpCodes.Brtrue_S, notNull); // relative stack [boxed value = null] il.Emit(OpCodes.Pop); // relative stack empty - il.Emit(OpCodes.Ldsfld, typeof(DBNull).GetField(nameof(DBNull.Value))!); // relative stack [DBNull] + if (handler is null) + { + il.Emit(OpCodes.Ldsfld, typeof(DBNull).GetField(nameof(DBNull.Value))!); // relative stack [DBNull] + } + else + { + il.Emit(OpCodes.Ldnull); // relative stack [null for the type handler] + } if (dbType == DbType.String || dbType == DbType.AnsiString) { EmitInt32(il, 0); diff --git a/Directory.Packages.props b/Directory.Packages.props index 2f91a3cca..b1e42ba41 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,6 +38,7 @@ + diff --git a/tests/Dapper.Tests/Dapper.Tests.csproj b/tests/Dapper.Tests/Dapper.Tests.csproj index 93b498618..e9eab6b88 100644 --- a/tests/Dapper.Tests/Dapper.Tests.csproj +++ b/tests/Dapper.Tests/Dapper.Tests.csproj @@ -32,6 +32,7 @@ + diff --git a/tests/Dapper.Tests/FakeDbTests.Issue1234.cs b/tests/Dapper.Tests/FakeDbTests.Issue1234.cs new file mode 100644 index 000000000..f4b9b3965 --- /dev/null +++ b/tests/Dapper.Tests/FakeDbTests.Issue1234.cs @@ -0,0 +1,56 @@ +#if !NET481 +using System; +using System.Data; +using pengdows.crud.fakeDb; +using Xunit; + +namespace Dapper.Tests; + +public sealed class FakeDbIssue1234Tests +{ + [Fact] + public void NullValueIsPassedToCustomTypeHandler() + { + SqlMapper.ResetTypeHandlers(); + var handler = new NullAwareHandler(); + SqlMapper.AddTypeHandler(handler); + try + { + using var connection = new fakeDbConnection(new FakeDataStore()); + connection.EnqueueNonQueryResult(1); + connection.Open(); + + connection.Execute("select @value", new { value = (NullAwareValue?)null }); + + Assert.True(handler.Called); + Assert.Null(handler.Value); + Assert.Equal(DbType.Binary, handler.LastParameter!.DbType); + Assert.Equal(DBNull.Value, handler.LastParameter.Value); + } + finally + { + SqlMapper.ResetTypeHandlers(); + } + } + + private sealed class NullAwareValue; + + private sealed class NullAwareHandler : SqlMapper.TypeHandler + { + public bool Called { get; private set; } + public NullAwareValue? Value { get; private set; } + public IDbDataParameter? LastParameter { get; private set; } + + public override NullAwareValue? Parse(object value) => null; + + public override void SetValue(IDbDataParameter parameter, NullAwareValue? value) + { + Called = true; + Value = value; + LastParameter = parameter; + parameter.DbType = DbType.Binary; + parameter.Value = DBNull.Value; + } + } +} +#endif