InvalidCastException long to ulong

时间:2012-06-27 12:40:23

标签: c# .net

我有以下方法:

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;
}

我想将MIN_ACTIVE_ROWVERSION数据库作为ulong。奇怪的是..下面的第一个方法调用生成错误,但第二个方法调用工作正常。

方法调用1 生成错误:

ulong minActiveRowversion = 
    SqlUtils.ExecuteScalar<ulong>(
        "SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
        , _connectionString);

错误:

System.InvalidCastException: Specified cast is not valid.

方法调用2 正常工作:

ulong minActiveRowversion = 
    (ulong)SqlUtils.ExecuteScalar<long>(
        "SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
        , _connectionString);

我不明白这是怎么可能的,因为command.ExecuteScalar()方法的结果是:

object result       | 1955612
result.GetType()    | {Name = "Int64" FullName = "System.Int64"}
  1. 有人可以告诉我为什么第一种情况无法实现,第二种情况有效吗?
  2. 有人可以告诉我如何我可以解决它,以便我可以使用方案1.

2 个答案:

答案 0 :(得分:5)

<强>为什么

您只能将值类型取消装入其原始类型。在您的情况下,演员首先需要从long然后object转到ulong

有关详细信息,请参阅此问题:

Why can't I unbox an int as a decimal?

它还链接了Eric Lippert的blog post

如何

正如您所知,一种方法是在转换为T之前转换为原始类型 - 当然,除非原始类型 T

正如评论中所提到的,另一种方法是使用转换例程(Convert.ToUInt64)而不是显式转换。

这可以使用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);
}

拨打电话:

ulong minActiveRowversion = 
    SqlUtils.ExecuteScalar<ulong>(
        Convert.ToUInt64,
        "SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
        , _connectionString);

答案 1 :(得分:4)

亚当的回答正确地指出了问题;这是一个解决方案:只要可以使用内置或自定义转换将{@ 1}}转换为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);
}

此方法生成一个LINQ表达式,该表达式首先将对象解包为其实际类型,然后应用转换。替换方法的最后一行

return (T)result;

return UnboxUnchecked<T>(result);

让它发挥作用。

以下是a link to an article,它解释了如何通过缓存已编译的lambdas来提高此类转换的效率。