diff --git a/Dapper/SqlMapper.cs b/Dapper/SqlMapper.cs index d23e949a5..e87a017bf 100644 --- a/Dapper/SqlMapper.cs +++ b/Dapper/SqlMapper.cs @@ -2666,7 +2666,11 @@ private static bool IsValueTuple(Type? type) => (type?.IsValueType == true { il.Emit(OpCodes.Ldloc, typedParameterLocal); // stack is now [parameters] [typed-param] il.Emit(callOpCode, prop.GetGetMethod()!); // stack is [parameters] [custom] - if (!prop.PropertyType.IsValueType) + if (prop.PropertyType.IsValueType) + { + il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [boxed-custom] + } + else { // throw if null var notNull = il.DefineLabel(); @@ -2678,7 +2682,7 @@ private static bool IsValueTuple(Type? type) => (type?.IsValueType == true } il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [custom] [command] il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [custom] [command] [name] - il.EmitCall(OpCodes.Callvirt, prop.PropertyType.GetMethod(nameof(ICustomQueryParameter.AddParameter))!, null); // stack is now [parameters] + il.EmitCall(OpCodes.Callvirt, typeof(ICustomQueryParameter).GetMethod(nameof(ICustomQueryParameter.AddParameter))!, null); // stack is now [parameters] continue; } #pragma warning disable 618 diff --git a/tests/Dapper.Tests/ParameterTests.cs b/tests/Dapper.Tests/ParameterTests.cs index 650624c20..5eb455c65 100644 --- a/tests/Dapper.Tests/ParameterTests.cs +++ b/tests/Dapper.Tests/ParameterTests.cs @@ -61,6 +61,21 @@ public void AddParameter(IDbCommand command, string name) } } + public readonly struct DbCustomParamStruct : SqlMapper.ICustomQueryParameter + { + private readonly IDbDataParameter _sqlParameter; + + public DbCustomParamStruct(IDbDataParameter sqlParameter) + { + _sqlParameter = sqlParameter; + } + + public void AddParameter(IDbCommand command, string name) + { + command.Parameters.Add(_sqlParameter); + } + } + private static IEnumerable CreateSqlDataRecordList(IDbCommand command, IEnumerable numbers) { #pragma warning disable CS0618 // Type or member is obsolete @@ -885,8 +900,23 @@ public void TestCustomParameterReuse() Assert.Equal(123, result2.Foo); Assert.Equal("abc", result2.Bar); } - - + + [Fact] + public void TestCustomParameterValueType() + { + // Value type (struct) ICustomQueryParameter previously caused a segfault + // because the IL emitted Callvirt on an unboxed struct (see #2189) + var args = new { + foo = new DbCustomParamStruct(Provider.CreateRawParameter("foo", 123)), + bar = "abc" + }; + var result = connection.Query("select Foo=@foo, Bar=@bar", args).Single(); + int foo = result.Foo; + string bar = result.Bar; + Assert.Equal(123, foo); + Assert.Equal("abc", bar); + } + [Fact] public void TestDynamicParamNullSupport() {