从int转换为long throws异常

时间:2015-10-30 08:01:10

标签: c# sql-server-2012

我有一个包含2列的表(string& int)。 int列的当前值为-1

使用SQLDataReader我正在读取此表中的行:

public class MyObject
{
    public string Name;
    public long Number;
}

using (SqlDataReader dataReader = command.ExecuteReader())
{
    MyObject o = new MyObject()
    while (dataReader.Read())
    {
        o.Name= dataReader.GetString(0);
        o.Number = dataReader.GetInt64(1);
    }
}

但是,在阅读InvalidCastException列时,我收到了Number。我在这里缺少什么?

5 个答案:

答案 0 :(得分:5)

SqlDataReader.GetInt64的评论部分指出:

  

不进行转换;因此,检索的数据必须已经是64位有符号整数。

顺便说一下:如果您的值可以为空,请不要忘记调用IsDBNUll

您确定数据库中检索到的数据是 bigint 吗?如果是int或nchar / nvarchar,则应使用相应的函数并将其转换或解析为System.Int64

有人建议改善代码的健壮性

  • 使用SqlDataReader [columnName]而不是GetInt64。这将为您提供原始类型的对象,该对象最有可能转换为您的长。
  • 使用列名而不是列号可以提高代码的稳定性。如果在查询中重新排序列,或者在查询中添加或删除了不同的列,它仍然有效
  • 如果你真的想坚持使用GetInt ...函数,请使用SqlDataReader.GetOrdinal(列名)来获取列号。
  • 在分配之前使用SqlDataReader.GetFieldType来检查列的类型并使用相应的GetInt ..函数

答案 1 :(得分:1)

您需要通过int将其作为GetInt32阅读。您不需要将其强制转换为long,因为运行时可以隐式地执行此操作。

运行时不会让你从object解除绑定到另一种数据类型,而无需先将object解除绑定到object的正确内部数据类型是,然后铸造到下一个。这个怪癖与数据阅读器无关:

static void Main(string[] args)
{
    int i = 1;

    object o = i;

    long l = (long)o; // invalid cast, trying to unbox to something other than the original type

    l = (long)(int)o; // No invalid cast, un-boxed to int then cast to long.
}

特别是使用SqlDataReader,因为您的基础类型不是Int64,它会在内部尝试直接解除错误类型:

internal Int64 Int64 {
    get {
        ThrowIfNull();

        if (StorageType.Int64 == _type) {
            return _value._int64;
        }
        return (Int64)this.Value; // anything else we haven't thought of goes through boxing.
    }
    // set excluded for brevity
}

取自http://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlBuffer.cs,1b180f5ee49fc7ee

TL; DR;做文件所说的(正如HaraldDutch的回答所建议的那样),并阅读正确的预期类型。

答案 2 :(得分:0)

确保您尝试获取的值在数据库中是bigint。 BIGINT相当于Int64(在C#中很长)。如果它不是bigint,请尝试使用Convert.ToInt64()

答案 3 :(得分:0)

好的,可以得出正确的结论。你的列DataType是int而不是bigint所以你不能通过.GetInt64()调用它,因为它是未装箱的

  

您无法取消选择Int32 - > Int64的

正如@Adam Houldsworth所说 -

  

"阅读给出的内容,而不是您想要的内容"

要解决您的问题,只需将GetInt64()切换为GetInt32()即可。见下文:

using (SqlDataReader dataReader = command.ExecuteReader())
{
    MyObject o = new MyObject()
    while (dataReader.Read())
    {
        o.Name= dataReader.GetString(0);
        o.Number = dataReader.GetInt32(1); // safe implicitly cast no need to make it manually
    }
}

如果您不了解确切的基础数据类型您可以随时以其他方式执行此操作。请参阅以下代码:

using (SqlDataReader dataReader = command.ExecuteReader())
{
    MyObject o = new MyObject()
    while (dataReader.Read())
    {
        o.Name = dataReader.GetString(0);
        var number = dataReader.GetValue(1);

        try
        {
            o.Number = Convert.ToInt64(number);
        }
        catch(InvalidCastException)
        {
            Throw new Exception("Underlying DataType is not convertable to int 64.");
        }
    }
}

答案 4 :(得分:0)

如果要将SQLDataReader对象从(object {int})开箱到很长一段时间,应该知道,没有直接的方法。例如此代码

long _result = (long)(sqlDR["IntTypeField"] ?? 0L);

将生成“ System.InvalidCastException”。 您应该使用更全面的功能,像这样

public static T NvlO<T>(object a, T b)
{
    if (a == null)
        return b;
    else
    {
        var lSrcType = a.GetType();
        var lDestType = typeof(T);

        if (lDestType.IsValueType && lDestType.IsAssignableFrom(lSrcType))
            return (T)a;
        var lDestTC = TypeDescriptor.GetConverter(typeof(T));
        if (lDestTC.CanConvertFrom(lSrcType))
            return (T)lDestTC.ConvertFrom(a);
        else
        {
            var lSrcTC = TypeDescriptor.GetConverter(lSrcType);
            String lTmp = lSrcTC.ConvertToInvariantString(a);
            return (T)lDestTC.ConvertFromInvariantString(lTmp);
        }
    }
}