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
相关产品推荐
相关产品推荐

