diff --git a/Dapper/SqlMapper.cs b/Dapper/SqlMapper.cs index 2fa0e72b7..a08dd19a9 100644 --- a/Dapper/SqlMapper.cs +++ b/Dapper/SqlMapper.cs @@ -2852,21 +2852,21 @@ private static bool IsValueTuple(Type? type) => (type?.IsValueType == true else { il.EmitCall(OpCodes.Callvirt, typeof(IDataParameter).GetProperty(nameof(IDataParameter.Value))!.GetSetMethod()!, null);// stack is now [parameters] [[parameters]] [parameter] - } - if (prop.PropertyType == typeof(string)) - { - var endOfSize = il.DefineLabel(); - var sizeLocal = GetSizeLocal(); - // don't set if 0 - il.Emit(OpCodes.Ldloc, sizeLocal); // [parameters] [[parameters]] [parameter] [size] - il.Emit(OpCodes.Brfalse_S, endOfSize); // [parameters] [[parameters]] [parameter] + if (prop.PropertyType == typeof(string)) + { + var endOfSize = il.DefineLabel(); + var sizeLocal = GetSizeLocal(); + // don't set if 0 + il.Emit(OpCodes.Ldloc, sizeLocal); // [parameters] [[parameters]] [parameter] [size] + il.Emit(OpCodes.Brfalse_S, endOfSize); // [parameters] [[parameters]] [parameter] - il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter] - il.Emit(OpCodes.Ldloc, sizeLocal); // stack is now [parameters] [[parameters]] [parameter] [parameter] [size] - il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty(nameof(IDbDataParameter.Size))!.GetSetMethod()!, null); // stack is now [parameters] [[parameters]] [parameter] + il.Emit(OpCodes.Dup);// stack is now [parameters] [[parameters]] [parameter] [parameter] + il.Emit(OpCodes.Ldloc, sizeLocal); // stack is now [parameters] [[parameters]] [parameter] [parameter] [size] + il.EmitCall(OpCodes.Callvirt, typeof(IDbDataParameter).GetProperty(nameof(IDbDataParameter.Size))!.GetSetMethod()!, null); // stack is now [parameters] [[parameters]] [parameter] - il.MarkLabel(endOfSize); + il.MarkLabel(endOfSize); + } } if (checkForDuplicates) { diff --git a/tests/Dapper.Tests/Providers/SqliteTests.cs b/tests/Dapper.Tests/Providers/SqliteTests.cs index b0c5e0f1b..553f2cad7 100644 --- a/tests/Dapper.Tests/Providers/SqliteTests.cs +++ b/tests/Dapper.Tests/Providers/SqliteTests.cs @@ -1,5 +1,6 @@ using Microsoft.Data.Sqlite; using System; +using System.Data; using System.Data.Common; using System.Linq; using System.Threading; @@ -65,6 +66,46 @@ public void Issue466_SqliteHatesOptimizations() Assert.Equal(42, row.Id); } + [FactSqlite] + public void TypeHandlerSetValue_StringParameterSizeIsNotOverwritten_Issue2141() + { + using var connection = GetSQLiteConnection(); + + SqlMapper.ResetTypeHandlers(); + SqlMapper.RemoveTypeMap(typeof(string)); + var handler = new SizedStringTypeHandler(); + SqlMapper.AddTypeHandler(handler); + try + { + // anonymous object parameters take the IL generator path, where the + // generated code used to overwrite the Size set by the handler + connection.Execute("select @p", new { p = "abc" }); + + Assert.NotNull(handler.LastParameter); + Assert.Equal(SizedStringTypeHandler.ExpectedSize, handler.LastParameter.Size); + } + finally + { + SqlMapper.ResetTypeHandlers(); + SqlMapper.AddTypeMap(typeof(string), DbType.String); + } + } + + private sealed class SizedStringTypeHandler : SqlMapper.TypeHandler + { + public const int ExpectedSize = 12345; + public IDbDataParameter? LastParameter { get; private set; } + + public override void SetValue(IDbDataParameter parameter, string? value) + { + parameter.Value = value; + parameter.Size = ExpectedSize; + LastParameter = parameter; + } + + public override string Parse(object value) => (string)value; + } + [FactSqlite] public async Task Issue466_SqliteHatesOptimizations_Async() {