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
B = 1
}
[FactUnlessCoreCLR("https://github.com/dotnet/corefx/issues/1613")]
[Fact]
public void AdoNetEnumValue()
{
using (var cmd = connection.CreateCommand())
{
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();
AnEnum val = (AnEnum)value;
val.IsEqualTo(AnEnum.B);
......@@ -2145,13 +2149,27 @@ public void DapperEnumValue_Mysql()
#endif
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);
var args = new DynamicParameters();
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);
// 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]
......
......@@ -61,7 +61,9 @@ public void AddParameter(IDbCommand command, string name)
}
var param = command.CreateParameter();
param.ParameterName = name;
#pragma warning disable 0618
param.Value = SqlMapper.SanitizeParameterValue(Value);
#pragma warning restore 0618
if (Length == -1 && Value != null && Value.Length <= DefaultLength)
{
param.Size = DefaultLength;
......
......@@ -267,7 +267,9 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
p.Direction = param.ParameterDirection;
if (handler == null)
{
#pragma warning disable 0618
p.Value = SqlMapper.SanitizeParameterValue(val);
#pragma warning restore 0618
if (dbType != null && p.DbType != dbType)
{
p.DbType = dbType.Value;
......
......@@ -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 is Enum)
......@@ -2296,11 +2298,45 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
bool checkForNull = true;
if (prop.PropertyType.IsValueType())
{
il.Emit(OpCodes.Box, prop.PropertyType); // stack is [parameters] [[parameters]] [parameter] [parameter] [boxed-value]
if (Nullable.GetUnderlyingType(prop.PropertyType) == null)
var propType = prop.PropertyType;
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
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)
{
......
......@@ -44,7 +44,9 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam
}
internal static void Set(IDbDataParameter parameter, DataTable table, string typeName)
{
#pragma warning disable 0618
parameter.Value = SqlMapper.SanitizeParameterValue(table);
#pragma warning restore 0618
if (string.IsNullOrEmpty(typeName) && table != null)
{
typeName = table.GetTypeName();
......
......@@ -28,7 +28,9 @@ object ITypeHandler.Parse(Type destinationType, object value)
void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
{
#pragma warning disable 0618
parameter.Value = SanitizeParameterValue(value);
#pragma warning restore 0618
if (parameter is System.Data.SqlClient.SqlParameter && !(value is DBNull))
{
((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