Commit b9288b4a authored by Marc Gravell's avatar Marc Gravell

Fix #468 - CreateParamInfoGenerator should treat enums as primitives, not box them as enums

parent 096b4ac0
...@@ -2104,13 +2104,17 @@ enum AnotherEnum : byte ...@@ -2104,13 +2104,17 @@ enum AnotherEnum : byte
B = 1 B = 1
} }
[FactUnlessCoreCLR("https://github.com/dotnet/corefx/issues/1613")] [Fact]
public void AdoNetEnumValue() public void AdoNetEnumValue()
{ {
using (var cmd = connection.CreateCommand()) using (var cmd = connection.CreateCommand())
{ {
cmd.CommandText = "select @foo"; cmd.CommandText = "select @foo";
cmd.Parameters.AddWithValue("@foo", AnEnum.B); var p = cmd.CreateParameter();
p.ParameterName = "@foo";
p.DbType = DbType.Int32; // it turns out that this is the key piece; setting the DbType
p.Value = AnEnum.B;
cmd.Parameters.Add(p);
object value = cmd.ExecuteScalar(); object value = cmd.ExecuteScalar();
AnEnum val = (AnEnum)value; AnEnum val = (AnEnum)value;
val.IsEqualTo(AnEnum.B); val.IsEqualTo(AnEnum.B);
...@@ -2145,13 +2149,27 @@ public void DapperEnumValue_Mysql() ...@@ -2145,13 +2149,27 @@ public void DapperEnumValue_Mysql()
#endif #endif
private static void DapperEnumValue(IDbConnection connection) private static void DapperEnumValue(IDbConnection connection)
{ {
var v = connection.QuerySingle<AnEnum>("select @v", new { v = AnEnum.B }); // test passing as AsEnum, reading as int
var v = (AnEnum)connection.QuerySingle<int>("select @v, @y, @z", new { v = AnEnum.B, y = (AnEnum?)AnEnum.B, z = (AnEnum?)null });
v.IsEqualTo(AnEnum.B); v.IsEqualTo(AnEnum.B);
var args = new DynamicParameters(); var args = new DynamicParameters();
args.Add("v", AnEnum.B); args.Add("v", AnEnum.B);
v = connection.QuerySingle<AnEnum>("select @v", args); args.Add("y", AnEnum.B);
args.Add("z", null);
v = (AnEnum)connection.QuerySingle<int>("select @v, @y, @z", args);
v.IsEqualTo(AnEnum.B); v.IsEqualTo(AnEnum.B);
// test passing as int, reading as AnEnum
var k = (int)connection.QuerySingle<AnEnum>("select @v, @y, @z", new { v = (int)AnEnum.B, y = (int?)(int)AnEnum.B, z = (int?)null });
k.IsEqualTo((int)AnEnum.B);
args = new DynamicParameters();
args.Add("v", (int)AnEnum.B);
args.Add("y", (int)AnEnum.B);
args.Add("z", null);
k = (int)connection.QuerySingle<AnEnum>("select @v, @y, @z", args);
k.IsEqualTo((int)AnEnum.B);
} }
[Fact] [Fact]
......
...@@ -61,7 +61,9 @@ public void AddParameter(IDbCommand command, string name) ...@@ -61,7 +61,9 @@ public void AddParameter(IDbCommand command, string name)
} }
var param = command.CreateParameter(); var param = command.CreateParameter();
param.ParameterName = name; param.ParameterName = name;
#pragma warning disable 0618
param.Value = SqlMapper.SanitizeParameterValue(Value); param.Value = SqlMapper.SanitizeParameterValue(Value);
#pragma warning restore 0618
if (Length == -1 && Value != null && Value.Length <= DefaultLength) if (Length == -1 && Value != null && Value.Length <= DefaultLength)
{ {
param.Size = DefaultLength; param.Size = DefaultLength;
......
...@@ -267,7 +267,9 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -267,7 +267,9 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
p.Direction = param.ParameterDirection; p.Direction = param.ParameterDirection;
if (handler == null) if (handler == null)
{ {
#pragma warning disable 0618
p.Value = SqlMapper.SanitizeParameterValue(val); p.Value = SqlMapper.SanitizeParameterValue(val);
#pragma warning restore 0618
if (dbType != null && p.DbType != dbType) if (dbType != null && p.DbType != dbType)
{ {
p.DbType = dbType.Value; p.DbType = dbType.Value;
......
...@@ -1964,7 +1964,9 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj ...@@ -1964,7 +1964,9 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj
} }
} }
internal static object SanitizeParameterValue(object value)
[Obsolete(ObsoleteInternalUsageOnly, false)]
public static object SanitizeParameterValue(object value)
{ {
if (value == null) return DBNull.Value; if (value == null) return DBNull.Value;
if (value is Enum) if (value is Enum)
...@@ -2296,11 +2298,45 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql) ...@@ -2296,11 +2298,45 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
bool checkForNull = true; bool checkForNull = true;
if (prop.PropertyType.IsValueType()) if (prop.PropertyType.IsValueType())
{ {
il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [[parameters]] [parameter] [parameter] [boxed-value] var propType = prop.PropertyType;
if (Nullable.GetUnderlyingType(prop.PropertyType) == null) var nullType = Nullable.GetUnderlyingType(propType);
bool callSanitize = false;
if((nullType ?? propType).IsEnum())
{
if(nullType != null)
{
// Nullable<SomeEnum>; we want to box as the underlying type; that's just *hard*; for
// simplicity, box as Nullable<SomeEnum> and call SanitizeParameterValue
callSanitize = true;
}
else
{
// non-nullable enum; we can do that! just box to the wrong type! (no, really)
switch (TypeExtensions.GetTypeCode(Enum.GetUnderlyingType(propType)))
{
case TypeCode.Byte: propType = typeof(byte); break;
case TypeCode.SByte: propType = typeof(sbyte); break;
case TypeCode.Int16: propType = typeof(short); break;
case TypeCode.Int32: propType = typeof(int); break;
case TypeCode.Int64: propType = typeof(long); break;
case TypeCode.UInt16: propType = typeof(ushort); break;
case TypeCode.UInt32: propType = typeof(uint); break;
case TypeCode.UInt64: propType = typeof(ulong); break;
}
}
}
else if (nullType == null)
{ // struct but not Nullable<T>; boxed value cannot be null { // struct but not Nullable<T>; boxed value cannot be null
checkForNull = false; checkForNull = false;
} }
il.Emit(OpCodes.Box, propType); // stack is [parameters] [[parameters]] [parameter] [parameter] [boxed-value]
if (callSanitize)
{
checkForNull = false; // handled by sanitize
il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod(nameof(SanitizeParameterValue)), null);
// stack is [parameters] [[parameters]] [parameter] [parameter] [boxed-value]
}
} }
if (checkForNull) if (checkForNull)
{ {
......
...@@ -44,7 +44,9 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam ...@@ -44,7 +44,9 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam
} }
internal static void Set(IDbDataParameter parameter, DataTable table, string typeName) internal static void Set(IDbDataParameter parameter, DataTable table, string typeName)
{ {
#pragma warning disable 0618
parameter.Value = SqlMapper.SanitizeParameterValue(table); parameter.Value = SqlMapper.SanitizeParameterValue(table);
#pragma warning restore 0618
if (string.IsNullOrEmpty(typeName) && table != null) if (string.IsNullOrEmpty(typeName) && table != null)
{ {
typeName = table.GetTypeName(); typeName = table.GetTypeName();
......
...@@ -28,7 +28,9 @@ object ITypeHandler.Parse(Type destinationType, object value) ...@@ -28,7 +28,9 @@ object ITypeHandler.Parse(Type destinationType, object value)
void ITypeHandler.SetValue(IDbDataParameter parameter, object value) void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
{ {
#pragma warning disable 0618
parameter.Value = SanitizeParameterValue(value); parameter.Value = SanitizeParameterValue(value);
#pragma warning restore 0618
if (parameter is System.Data.SqlClient.SqlParameter && !(value is DBNull)) if (parameter is System.Data.SqlClient.SqlParameter && !(value is DBNull))
{ {
((System.Data.SqlClient.SqlParameter)parameter).SqlDbType = SqlDbType.Udt; ((System.Data.SqlClient.SqlParameter)parameter).SqlDbType = SqlDbType.Udt;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment