[英]InvalidCastException long to ulong
I have the following method: 我有以下方法:
public static T ExecuteScalar<T>(
string query,
SqlConnection connection,
params SqlParameter[] parameters) where T : new()
{
// Create SqlCommand
SqlCommand command = CreateCommand(query, connection, parameters);
// Execute command using ExecuteScalar
object result = command.ExecuteScalar();
// Return value as expected type
if (result == null || result is DBNull) return default(T);
return (T)result;
}
I want to have the MIN_ACTIVE_ROWVERSION
of the database as an ulong
. 我希望将数据库的MIN_ACTIVE_ROWVERSION
作为ulong
。 The strange thing is.. the First method call below generates an error but the second method call works fine. 奇怪的是..下面的第一个方法调用生成错误,但第二个方法调用工作正常。
Method call 1 generates an error: 方法调用1生成错误:
ulong minActiveRowversion =
SqlUtils.ExecuteScalar<ulong>(
"SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
, _connectionString);
Error: 错误:
System.InvalidCastException: Specified cast is not valid.
Method call 2 works fine: 方法调用2工作正常:
ulong minActiveRowversion =
(ulong)SqlUtils.ExecuteScalar<long>(
"SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
, _connectionString);
I don't understand how that is possible because the result of the command.ExecuteScalar()
method is this: 我不明白这是怎么可能的,因为command.ExecuteScalar()
方法的结果是这样的:
object result | 1955612
result.GetType() | {Name = "Int64" FullName = "System.Int64"}
Why 为什么
You can only unbox a value type to it's original type. 您只能将值类型取消装入其原始类型。 In your case, the cast first needs to go to long
from object
and then to ulong
. 在你的情况下,演员首先需要从object
转到long
然后再到ulong
。
See this question for more detail: 有关详细信息,请参阅此问题:
Why can't I unbox an int as a decimal? 为什么我不能将int取消装入十进制?
It also links a blog post by Eric Lippert. 它还链接了Eric Lippert的博客文章 。
How 怎么样
One way, as you know, is to cast to the original type before casting to T
- unless, of course, the original type is T
. 如你所知,一种方法是在转换为T
之前转换为原始类型 - 当然,除非原始类型为 T
As mentioned in the comments, another way is to use conversion routines ( Convert.ToUInt64
) and not explicit casting. 正如评论中所提到的,另一种方法是使用转换例程( Convert.ToUInt64
)而不是显式转换。
This could potentially be achieved using a Func<object, T>
: 这可以使用Func<object, T>
来实现:
public static T ExecuteScalar<T>(
Func<object, T> conversionFunctor,
string query,
SqlConnection connection,
params SqlParameter[] parameters) where T : new()
{
// Create SqlCommand
SqlCommand command = CreateCommand(query, connection, parameters);
// Execute command using ExecuteScalar
object result = command.ExecuteScalar();
// Return value as expected type
if (result == null || result is DBNull)
return default(T);
return conversionFunctor(result);
}
Making your call: 打电话:
ulong minActiveRowversion =
SqlUtils.ExecuteScalar<ulong>(
Convert.ToUInt64,
"SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
, _connectionString);
Adam's answer correctly identifies the problem; 亚当的答案正确地确定了问题; here is a solution: you can use LINQ to unbox any type, as long as it can be cast to T
with a built-in or a custom conversion. 这是一个解决方案:只要可以使用内置或自定义转换将其转换为T
,您可以使用LINQ取消任何类型的转换。
static T UnboxUnchecked<T>(object obj) {
var pe = Expression.Parameter(typeof(object));
return Expression.Lambda<Func<object,T>>(
Expression.Convert(
Expression.Convert(pe, obj.GetType())
, typeof (T)
)
, pe
).Compile()(obj);
}
This method produces a LINQ expression that first unboxes the object to its actual type, and then applies the conversion. 此方法生成一个LINQ表达式,首先将对象解包为其实际类型,然后应用转换。 Replace the last line of your method 替换方法的最后一行
return (T)result;
with 同
return UnboxUnchecked<T>(result);
to make it work. 使它工作。
Here is a link to an article that explains how to make conversions of this kind more efficient by caching the compiled lambdas. 以下是一篇文章的链接,该文章解释了如何通过缓存已编译的lambda来提高此类转换的效率。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.