使用Npgsql时字符串转数值/DateTime类型失败的问题排查
Npgsql类型转换错误排查:System.InvalidCastException字符串转Numeric失败
错误现象
执行代码时反复触发以下异常:
System.InvalidCastException: 'Can't write CLR type System.String with handler type NumericHandler'
错误本质明确:向PostgreSQL的Numeric类型字段传入了CLR的String类型值,二者类型不匹配。
场景背景
正在将旧VB代码迁移至C#,VB中用于存储数值的变量初始声明为字符串,且包含字符串操作逻辑。计划先完成字符串处理,再通过Convert.ToDecimal()转换为数值类型后传入Npgsql参数,但执行报错。
代码问题分析
初始变量声明
string tno = ""; string iw = ""; string ws = ""; string ftg = "0"; string hl = "0"; string ll = "0"; string date = "2012/02/04"; string va = "0"; string dev = "0";
尝试的转换操作
Convert.ToDecimal(tno);
SQL语句
string sqlString = @"UPDATE header SET ft = @gftg, high_limit=@ghl, low_limit=@gll, start_date = @gsdd, avg_ga = @gvgga, std_dev = @gstd WHERE cmpy_id = 'ABC' and tag_no = @gto and ip_num = @gim";
Npgsql参数绑定(错误代码)
cmd.Parameters.Add("@gim", NpgsqlTypes.NpgsqlDbType.Varchar).Value = iw; cmd.Parameters.Add("@gftg", NpgsqlTypes.NpgsqlDbType.Numeric).Value = ftg; cmd.Parameters.Add("@ghl", NpgsqlTypes.NpgsqlDbType.Numeric).Value = hl; cmd.Parameters.Add("@gll", NpgsqlTypes.NpgsqlDbType.Numeric).Value = ll; cmd.Parameters.Add("@gsdd", NpgsqlTypes.NpgsqlDbType.Date).Value = date; cmd.Parameters.Add("@gvgga", NpgsqlTypes.NpgsqlDbType.Numeric).Value = va; cmd.Parameters.Add("@gstd", NpgsqlTypes.NpgsqlDbType.Numeric).Value = dev;
核心问题
- 转换操作未生效:
Convert.ToDecimal(tno)仅执行了转换计算,但未将结果赋值给变量。原变量ftg、hl等仍为字符串类型,绑定参数时传入的还是字符串,导致类型不匹配。 - 错误的变量声明尝试:直接用字符串字面量初始化
decimal变量会触发编译错误,C#不支持这种隐式转换:
// 编译错误:无法将字符串转换为decimal decimal ftg = "0";
解决方案
正确的转换与参数绑定
转换时必须将结果赋值给变量,再将转换后的数值类型变量绑定到参数:
// 先完成字符串处理逻辑... // 转换为decimal类型并赋值 decimal ftgDecimal = Convert.ToDecimal(ftg); decimal hlDecimal = Convert.ToDecimal(hl); decimal llDecimal = Convert.ToDecimal(ll); decimal vaDecimal = Convert.ToDecimal(va); decimal devDecimal = Convert.ToDecimal(dev); // 日期需转换为DateTime类型,不能直接传字符串 DateTime dateValue = DateTime.Parse(date); // 绑定参数时传入转换后的数值/日期类型 cmd.Parameters.Add("@gim", NpgsqlTypes.NpgsqlDbType.Varchar).Value = iw; cmd.Parameters.Add("@gftg", NpgsqlTypes.NpgsqlDbType.Numeric).Value = ftgDecimal; cmd.Parameters.Add("@ghl", NpgsqlTypes.NpgsqlDbType.Numeric).Value = hlDecimal; cmd.Parameters.Add("@gll", NpgsqlTypes.NpgsqlDbType.Numeric).Value = llDecimal; cmd.Parameters.Add("@gsdd", NpgsqlTypes.NpgsqlDbType.Date).Value = dateValue; cmd.Parameters.Add("@gvgga", NpgsqlTypes.NpgsqlDbType.Numeric).Value = vaDecimal; cmd.Parameters.Add("@gstd", NpgsqlTypes.NpgsqlDbType.Numeric).Value = devDecimal;
可选优化:提前使用正确类型
如果字符串操作逻辑可调整,建议直接将变量声明为对应类型,减少转换步骤:
string tno = ""; string iw = ""; string ws = ""; decimal ftg = 0; decimal hl = 0; decimal ll = 0; DateTime date = new DateTime(2012, 2, 4); decimal va = 0; decimal dev = 0; // 若需字符串操作,可临时转换为字符串处理后再转回decimal // 例如: ftg = decimal.Parse(ftg.ToString().Replace(",", "."));
关键说明
- PostgreSQL的
Numeric类型优先对应CLR的decimal类型(可保证精度,适合数值计算场景),double也可兼容,但存在精度丢失风险。 - 日期类型不能直接传字符串,必须转换为
DateTime类型后传入,否则会触发类似的类型转换错误。
内容的提问来源于stack exchange,提问作者austin-smith-999
相关产品推荐
相关产品推荐

