You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#调用SqlDataReader.GetDouble为何仍抛出InvalidCast异常

C# SQL查询读取数据时抛出InvalidCast异常修复

问题场景

在基于.NET Framework开发Windows窗体应用的C#程序中,编写代码实现数据库连接、读取SQL Server查询结果并存储到列表供后续使用,确认返回类型与变量声明匹配的前提下,运行时仍持续抛出InvalidCast异常。
原始代码如下:

public List<Info> GetInfo(string Col)
{
    string connectionString = "Server=VIS-12\TESTSQLSERVER;Database=SPCC;User Id=sa;Password=admin1234;";
    string comand = $"select {Col} from Tbl_Reading where [LogTime] between '2017-07-06 14:30:26.000' and '2017-07-06 14:30:26.000' ";
    using (SqlConnection conn = new SqlConnection())
    {
        double[] val1 = new double [100] ;
        conn.ConnectionString = connectionString;
        conn.Open();
        SqlCommand c1 = new SqlCommand(comand, conn);
        SqlDataReader reader = c1.ExecuteReader();

        if (reader.HasRows)
        {
            while (reader.Read())
            {
                int x = 0;
                val1[x] = Convert.ToDouble(reader.GetDouble(0)); // 此处抛出异常
                x++;
            }
        }
        reader.Close();
        List<Info> d1 = new List<Info>();
        d1 = (List<Info>)val1.Cast<Info>();//ToList();
        conn.Close();
        return d1;
        //return val1;
    }
}

异常根因

代码里一共存在2处会触发/必然触发InvalidCast的问题,还有若干逻辑bug:

  • 读取数据行处的异常:reader.GetDouble(0)要求查询返回的第0列原生类型必须是SQL Server的float/real类型,如果列实际是decimal/numeric/varchar等其他类型,直接调用该方法会直接抛出类型转换异常。
  • 数组转List处的必然异常:val1.Cast<Info>()仅支持类型的引用转换、装箱/拆箱转换,double是值类型,和自定义类Info没有任何继承或转换关系,执行强转时必然抛出InvalidCast,就算加上注释里的.ToList()也无法解决该问题。
  • 其他逻辑问题:
    • 循环内声明int x=0,导致所有读取到的值都会覆盖数组索引0的位置,完全无法正确存储多行数据。
    • 固定初始化长度为100的double数组,查询结果超过100行会触发索引越界,不足100行会残留大量默认值0的脏数据。
    • 连接字符串中实例名的反斜杠未转义,C#会将\T识别为转义字符,会导致数据库连接失败。
    • SqlCommand、SqlDataReader未放入using块,存在资源泄漏风险。
    • SQL语句直接拼接列名参数,存在SQL注入风险。
    • 查询条件中between的两个时间完全一致,仅会返回LogTime精确等于该时间点的记录,大概率不符合业务预期。

修复后代码

首先确认Info类的定义,示例如下(根据你自己的实际类结构调整赋值逻辑即可):

public class Info
{
    // 存储读取到的列数值
    public double ColumnValue { get; set; }
}

修复后的完整实现:

public List<Info> GetInfo(string Col)
{
    // 字符串前加@取消反斜杠转义,避免连接字符串解析错误
    string connectionString = @"Server=VIS-12\TESTSQLSERVER;Database=SPCC;User Id=sa;Password=admin1234;";
    // 生产环境建议对列名做白名单校验,避免SQL注入风险
    string sql = $"select {Col} from Tbl_Reading where [LogTime] between '2017-07-06 14:30:26.000' and '2017-07-06 14:30:26.000' ";
    
    List<Info> result = new List<Info>();
    // 所有实现IDisposable的数据库对象都放入using块,自动释放资源
    using (SqlConnection conn = new SqlConnection(connectionString))
    {
        conn.Open();
        using (SqlCommand cmd = new SqlCommand(sql, conn))
        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                // 跳过NULL值避免转换报错,不需要单独判断HasRows
                if (reader.IsDBNull(0)) continue;
                // 用索引器取值+Convert.ToDouble,兼容数据库端不同的数值类型
                double val = Convert.ToDouble(reader[0]);
                // 直接构造Info对象加入列表,不需要固定长度数组中转
                result.Add(new Info { ColumnValue = val });
            }
        }
    }
    return result;
}

注意:如果你的Info类有其他字段或不同的属性定义,只需要调整new Info()时的赋值逻辑即可,不需要用数组做中间存储。

内容的提问来源于stack exchange,提问作者TheLostChild

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 07:24:24