使用.NET Dapper,我遇到一个问题,即获取一个包含整数值(0/1)的数据库字段以映射到类中的可为空的布尔属性。
为了简单起见,我已经精简了一下,并将该类重命名为重现该问题所需的最低要求:
public class Test
{
public bool? TestField { get; set; }
}
如果调用以下代码来填充Test类:
var Results = DBConnection.Query<Test>("SELECT 0 As TestField]").ToList();
将引发以下错误:
Invalid cast from 'System.Int32' to 'System.Nullable`1[[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
如果我删除问号,将字段设置为非空布尔值(即public bool TestField),则一切正常。
即时答案可能似乎删除了可为空的内容,并将其命名为“day”。但是,行不通的原因是因为我们使用同一类在Web服务之间对记录进行序列化,并且我们需要能够分辨出false和null之间的区别。我想到了两个类,一个类具有可为空的属性类型,而一个类则没有,但是然后我不得不维护两个类而不是一个类。
在属性集期间进行自定义数据转换将是理想的。虽然,在dapper文档中没有发现任何暗示这一点的可能性。
最佳答案
看起来Dapper代码中可能存在关于可为null的布尔值/长整数等问题。
这是源代码中的三行(行2375-2377版本1.12.1.1)。问题在第一行:
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]
发出此代码后,它等效于以下代码行:
Convert.ChangeType(0, typeof(bool?));
不幸的是,这引发了我所看到的错误。通过将上面的第一条il.Emit()行更改为以下内容:
il.Emit(OpCodes.Ldtoken, nullUnderlyingType ?? memberType); // stack is now [target][target][value][member-type-token]
生成的等效代码行变成了这一行,请注意最后的typeof(bool?)之后不再具有可为空的问号:
Convert.ChangeType(0, typeof(bool));
这行代码不会引发错误。
因此,解决方案是重新编译源代码。我会将更改提交给项目,以供他们查看,看是否会引起任何不良的副作用。