Skip to content

Commit c46c69a

Browse files
authored
1. give clearer error messages when an ICustomQueryParameter is null (#2003)
2. provide a convenience .ctor on DbString
1 parent 0272d82 commit c46c69a

File tree

4 files changed

+59
-3
lines changed

4 files changed

+59
-3
lines changed

Dapper/DbString.cs

+15-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ public DbString()
2828
Length = -1;
2929
IsAnsi = IsAnsiDefault;
3030
}
31+
32+
/// <summary>
33+
/// Create a new DbString
34+
/// </summary>
35+
public DbString(string? value, int length = -1)
36+
{
37+
Value = value;
38+
Length = length;
39+
IsAnsi = IsAnsiDefault;
40+
}
41+
3142
/// <summary>
3243
/// Ansi vs Unicode
3344
/// </summary>
@@ -44,12 +55,13 @@ public DbString()
4455
/// The value of the string
4556
/// </summary>
4657
public string? Value { get; set; }
47-
58+
4859
/// <summary>
4960
/// Gets a string representation of this DbString.
5061
/// </summary>
51-
public override string ToString() =>
52-
$"Dapper.DbString (Value: '{Value}', Length: {Length}, IsAnsi: {IsAnsi}, IsFixedLength: {IsFixedLength})";
62+
public override string ToString() => Value is null
63+
? $"Dapper.DbString (Value: null, Length: {Length}, IsAnsi: {IsAnsi}, IsFixedLength: {IsFixedLength})"
64+
: $"Dapper.DbString (Value: '{Value}', Length: {Length}, IsAnsi: {IsAnsi}, IsFixedLength: {IsFixedLength})";
5365

5466
/// <summary>
5567
/// Add the parameter to the command... internal use only

Dapper/PublicAPI.Shipped.txt

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Dapper.CustomPropertyTypeMap.GetMember(string! columnName) -> Dapper.SqlMapper.I
3030
Dapper.DbString
3131
Dapper.DbString.AddParameter(System.Data.IDbCommand! command, string! name) -> void
3232
Dapper.DbString.DbString() -> void
33+
Dapper.DbString.DbString(string? value, int length = -1) -> void
3334
Dapper.DbString.IsAnsi.get -> bool
3435
Dapper.DbString.IsAnsi.set -> void
3536
Dapper.DbString.IsFixedLength.get -> bool
@@ -323,6 +324,7 @@ static Dapper.SqlMapper.Settings.UseSingleRowOptimization.set -> void
323324
static Dapper.SqlMapper.SetTypeMap(System.Type! type, Dapper.SqlMapper.ITypeMap? map) -> void
324325
static Dapper.SqlMapper.SetTypeName(this System.Data.DataTable! table, string! typeName) -> void
325326
static Dapper.SqlMapper.ThrowDataException(System.Exception! ex, int index, System.Data.IDataReader! reader, object? value) -> void
327+
static Dapper.SqlMapper.ThrowNullCustomQueryParameter(string! name) -> void
326328
static Dapper.SqlMapper.TypeHandlerCache<T>.Parse(object! value) -> T?
327329
static Dapper.SqlMapper.TypeHandlerCache<T>.SetValue(System.Data.IDbDataParameter! parameter, object! value) -> void
328330
static Dapper.SqlMapper.TypeMapProvider -> System.Func<System.Type!, Dapper.SqlMapper.ITypeMap!>!

Dapper/SqlMapper.cs

+19
Original file line numberDiff line numberDiff line change
@@ -2637,6 +2637,16 @@ private static bool IsValueTuple(Type? type) => (type?.IsValueType == true
26372637
{
26382638
il.Emit(OpCodes.Ldloc, typedParameterLocal); // stack is now [parameters] [typed-param]
26392639
il.Emit(callOpCode, prop.GetGetMethod()!); // stack is [parameters] [custom]
2640+
if (!prop.PropertyType.IsValueType)
2641+
{
2642+
// throw if null
2643+
var notNull = il.DefineLabel();
2644+
il.Emit(OpCodes.Dup); // stack is [parameters] [custom] [custom]
2645+
il.Emit(OpCodes.Brtrue_S, notNull); // stack is [parameters] [custom]
2646+
il.Emit(OpCodes.Ldstr, prop.Name); // stack is [parameters] [custom] [name]
2647+
il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod(nameof(ThrowNullCustomQueryParameter))!, null); // stack is [parameters] [custom]
2648+
il.MarkLabel(notNull);
2649+
}
26402650
il.Emit(OpCodes.Ldarg_0); // stack is now [parameters] [custom] [command]
26412651
il.Emit(OpCodes.Ldstr, prop.Name); // stack is now [parameters] [custom] [command] [name]
26422652
il.EmitCall(OpCodes.Callvirt, prop.PropertyType.GetMethod(nameof(ICustomQueryParameter.AddParameter))!, null); // stack is now [parameters]
@@ -3859,6 +3869,14 @@ private static void FlexibleConvertBoxedFromHeadOfStack(ILGenerator il, Type fro
38593869
return null;
38603870
}
38613871

3872+
/// <summary>
3873+
/// For internal use only
3874+
/// </summary>
3875+
[Obsolete(ObsoleteInternalUsageOnly, false)]
3876+
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
3877+
public static void ThrowNullCustomQueryParameter(string name)
3878+
=> throw new InvalidOperationException($"Member '{name}' is an {nameof(ICustomQueryParameter)} and cannot be null");
3879+
38623880
/// <summary>
38633881
/// Throws a data exception, only used internally
38643882
/// </summary>
@@ -3867,6 +3885,7 @@ private static void FlexibleConvertBoxedFromHeadOfStack(ILGenerator il, Type fro
38673885
/// <param name="reader">The reader the exception occurred in.</param>
38683886
/// <param name="value">The value that caused the exception.</param>
38693887
[Obsolete(ObsoleteInternalUsageOnly, false)]
3888+
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
38703889
public static void ThrowDataException(Exception ex, int index, IDataReader reader, object? value)
38713890
{
38723891
Exception toThrow;

tests/Dapper.Tests/MiscTests.cs

+23
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,27 @@ public void TestDbString()
657657
Assert.Equal(10, (int)obj.f);
658658
}
659659

660+
[Fact]
661+
public void DbStringNullHandling()
662+
{
663+
// without lengths
664+
var obj = new { x = new DbString("abc"), y = (DbString?)new DbString(null) };
665+
var row = connection.QuerySingle<(string? x,string? y)>("select @x as x, @y as y", obj);
666+
Assert.Equal("abc", row.x);
667+
Assert.Null(row.y);
668+
669+
// with lengths
670+
obj = new { x = new DbString("abc", 200), y = (DbString?)new DbString(null, 200) };
671+
row = connection.QuerySingle<(string? x, string? y)>("select @x as x, @y as y", obj);
672+
Assert.Equal("abc", row.x);
673+
Assert.Null(row.y);
674+
675+
// null raw value - give clear message, at least
676+
obj = obj with { y = null };
677+
var ex = Assert.Throws<InvalidOperationException>(() => connection.QuerySingle<(string? x, string? y)>("select @x as x, @y as y", obj));
678+
Assert.Equal("Member 'y' is an ICustomQueryParameter and cannot be null", ex.Message);
679+
}
680+
660681
[Fact]
661682
public void TestDbStringToString()
662683
{
@@ -668,6 +689,8 @@ public void TestDbStringToString()
668689
new DbString { Value = "abcde", IsFixedLength = false, Length = 10, IsAnsi = true }.ToString());
669690
Assert.Equal("Dapper.DbString (Value: 'abcde', Length: 10, IsAnsi: False, IsFixedLength: False)",
670691
new DbString { Value = "abcde", IsFixedLength = false, Length = 10, IsAnsi = false }.ToString());
692+
Assert.Equal("Dapper.DbString (Value: null, Length: -1, IsAnsi: False, IsFixedLength: False)",
693+
new DbString { Value = null }.ToString());
671694

672695
Assert.Equal("Dapper.DbString (Value: 'abcde', Length: -1, IsAnsi: True, IsFixedLength: False)",
673696
new DbString { Value = "abcde", IsAnsi = true }.ToString());

0 commit comments

Comments
 (0)