Commit da2334ac authored by Marc Gravell's avatar Marc Gravell

IL level type-remapping, so i8 / i4 are interchangeable, etc. This avoids...

IL level type-remapping, so i8 / i4 are interchangeable, etc. This avoids issues on databases that return 1 as bigint by default, etc
parent c6735ebc
......@@ -1886,8 +1886,68 @@ public static void SetTypeMap(Type type, ITypeMap map)
}
else
{
Type dataType = reader.GetFieldType(index);
TypeCode dataTypeCode = Type.GetTypeCode(dataType), unboxTypeCode = Type.GetTypeCode(unboxType);
if (dataType == unboxType || dataTypeCode == unboxTypeCode || dataTypeCode == Type.GetTypeCode(nullUnderlyingType))
{
il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
}
else
{
// not a direct match; need to tweak the unbox
bool handled = true;
OpCode opCode = default(OpCode);
if (dataTypeCode == TypeCode.Decimal || unboxTypeCode == TypeCode.Decimal)
{ // no IL level conversions to/from decimal; I guess we could use the static operators, but
// this feels an edge-case
handled = false;
}
else
{
switch (unboxTypeCode)
{
case TypeCode.Byte:
opCode = OpCodes.Conv_Ovf_I1_Un; break;
case TypeCode.SByte:
opCode = OpCodes.Conv_Ovf_I1; break;
case TypeCode.UInt16:
opCode = OpCodes.Conv_Ovf_I2_Un; break;
case TypeCode.Int16:
opCode = OpCodes.Conv_Ovf_I2; break;
case TypeCode.UInt32:
opCode = OpCodes.Conv_Ovf_I4_Un; break;
case TypeCode.Boolean: // boolean is basically an int, at least at this level
case TypeCode.Int32:
opCode = OpCodes.Conv_Ovf_I4; break;
case TypeCode.UInt64:
opCode = OpCodes.Conv_Ovf_I8_Un; break;
case TypeCode.Int64:
opCode = OpCodes.Conv_Ovf_I8; break;
case TypeCode.Single:
opCode = OpCodes.Conv_R4; break;
case TypeCode.Double:
opCode = OpCodes.Conv_R8; break;
default:
handled = false;
break;
}
}
if (handled)
{ // unbox as the data-type, then use IL-level convert
il.Emit(OpCodes.Unbox_Any, dataType); // stack is now [target][target][data-typed-value]
il.Emit(opCode); // stack is now [target][target][typed-value]
}
else
{ // use flexible conversion
il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [target][target][value][member-type-token]
il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null); // stack is now [target][target][value][member-type]
il.EmitCall(OpCodes.Call, typeof(Convert).GetMethod("ChangeType", new Type[] {typeof(object), typeof(Type)}),null); // stack is now [target][target][boxed-member-type-value]
il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
}
}
}
}
if (specializedConstructor == null)
{
......
......@@ -1618,16 +1618,10 @@ public void TestNullableUniqueIdentifierNull()
}
public void TestFailInASaneWayWithWrongStructColumnTypes()
public void WorkDespiteHavingWrongStructColumnTypes()
{
try
{
connection.Query<CanHazInt>("select cast(1 as bigint) Value").Single();
throw new Exception("Should not have got here");
} catch(DataException ex)
{
ex.Message.IsEqualTo("Error parsing column 0 (Value=1 - Int64)");
}
var hazInt = connection.Query<CanHazInt>("select cast(1 as bigint) Value").Single();
hazInt.Value.Equals(1);
}
......@@ -1896,6 +1890,32 @@ public class TypeWithMapping
public string B { get; set; }
}
public class WrongTypes
{
public int A { get; set; }
public double B { get; set; }
public long C { get; set; }
public bool D { get; set; }
}
public void TestWrongTypes_WithRightTypes()
{
var item = connection.Query<WrongTypes>("select 1 as A, cast(2.0 as float) as B, cast(3 as bigint) as C, cast(1 as bit) as D").Single();
item.A.Equals(1);
item.B.Equals(2.0);
item.C.Equals(3L);
item.D.Equals(true);
}
public void TestWrongTypes_WithWrongTypes()
{
var item = connection.Query<WrongTypes>("select cast(1.0 as float) as A, 2 as B, 3 as C, cast(1 as bigint) as D").Single();
item.A.Equals(1);
item.B.Equals(2.0);
item.C.Equals(3L);
item.D.Equals(true);
}
class TransactedConnection : IDbConnection
{
IDbConnection _conn;
......
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