Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InvalidCastException long to ulong

Tags:

c#

.net

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. 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:

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:

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:

object result       | 1955612
result.GetType()    | {Name = "Int64" FullName = "System.Int64"}
  1. Can someone tell me why the first scenario is not possible and the second scenario works?
  2. Can someone tell me how I can solve it so I can use scenario 1.
like image 667
hwcverwe Avatar asked Jun 27 '12 12:06

hwcverwe


People also ask

How do you convert long to Ulong?

To convert a long to a ulong, simply cast it: long a; ulong b = (ulong)a; C# will NOT throw an exception if it is a negative number.

Is a long an Int64?

In C#, long is mapped to Int64. It is a value type and represent System. Int64 struct. It is signed and takes 64 bits.


2 Answers

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.

See this question for more detail:

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

It also links a blog post by 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.

As mentioned in the comments, another way is to use conversion routines (Convert.ToUInt64) and not explicit casting.

This could potentially be achieved using a 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);
like image 107
Adam Houldsworth Avatar answered Sep 28 '22 09:09

Adam Houldsworth


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.

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. 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.

like image 41
Sergey Kalinichenko Avatar answered Sep 28 '22 10:09

Sergey Kalinichenko