C#操作PostgreSQL select查询传值WinForm时出现数据转换错误
问题现象
在数据库执行查询并将结果数据传递至Windows Forms窗体实例时,出现数据转换错误,错误截图如下:
相关实现代码如下:
- 编辑按钮点击事件逻辑代码:
private void msi_editar_Click(object sender, EventArgs e) { try { string codusu = dgv_usuarios.SelectedRows[0].Cells[0].Value.ToString(); dt = Connection.select($@"select * from view_coa_cadusu where codusu = {codusu}"); this.Hide(); ManipulaUsuario mu = new ManipulaUsuario( dt.Rows[0].Field<String>("coa_nomcomp"), dt.Rows[0].Field<String>("nomusu"), dt.Rows[0].Field<String>("senusu"), dt.Rows[0].Field<String>("coa_emailusu"), dt.Rows[0].Field<Char>("ativo"), dt.Rows[0].Field<String>("coa_telusu"), dt.Rows[0].Field<String>("coa_endusu"), dt.Rows[0].Field<String>("coa_numendusu"), dt.Rows[0].Field<String>("coa_baiendusu"), dt.Rows[0].Field<String>("coa_complendusu"), dt.Rows[0].Field<String>("coa_cependusu") ); mu.FormClosed += (s, args) => this.Show(); mu.Text = "Edição de usuário"; mu.ShowDialog(); } catch (ArgumentOutOfRangeException ex) { MessageBox.Show("Selecione um usuário válido", "Erro de seleção"); return; } }
- 数据库侧使用的查询视图定义:
SELECT cadusu.codusu, cadusu.nomusu, cadusu.senusu, COALESCE(cadusu.nomcompusu, ''::character varying) AS coa_nomcomp, cadusu.ativo, COALESCE(cadusu.emailusu, ''::character varying) AS coa_emailusu, COALESCE(cadusu.telusu, ''::character varying) AS coa_telusu, COALESCE(cadusu.endusu, ''::character varying) AS coa_endusu, COALESCE(cadusu.numendusu, ''::character varying) AS coa_numenusu, COALESCE(cadusu.baiendusu, ''::character varying) AS coa_baiendusu, COALESCE(cadusu.complendusu, ''::character varying) AS coa_complendusu, COALESCE(cadusu.cependusu, ''::character varying) AS coa_cependusu FROM cadusu;
- 被调用的Windows窗体构造函数代码:
public ManipulaUsuario() { InitializeComponent(); } public ManipulaUsuario(string nomcompusu, string nomusu, string senusu, string emailusu, char ativo, string telusu, string endusu, string numendusu, string baiendusu, string complendusu, string cependusu) : this() { lb_ativo.Visible = true; rb_ativo.Visible = true; rb_inativo.Visible = true; tb_nome.Text = nomcompusu; tb_usuario.Text = nomusu; tb_senha.Text = senusu; tb_email.Text = emailusu; tb_telefone.Text = telusu; tb_endereco.Text = endusu; tb_numero_endereco.Text = numendusu; tb_bairro.Text = baiendusu; tb_complemento.Text = complendusu; tb_cep.Text = cependusu; if (ativo == 'S') { rb_ativo.Checked = true; } else { rb_inativo.Checked = true; } }
预期实现效果为查询完成后将数据库返回的数据传递至目标窗体对应控件中,在每个字段取值后追加.ToString()方法后问题仍未解决。
错误根因
核心问题出在字段类型映射不匹配,附带其他逻辑隐患:
- 直接触发报错的原因:视图中
cadusu.ativo字段未做类型转换,PostgreSQL中存储单字符标识的character(1)/char类型,通过Npgsql驱动读取到DataTable时,默认对应C#的string类型(长度为1的字符串),而非char值类型。代码中直接调用dt.Rows[0].Field<Char>("ativo")做强类型读取,类型不匹配会直接抛出无效转换异常。之前追加.ToString()的方案没有生效,是因为Field<T>方法在取值阶段就已经抛出异常,根本不会执行到后续的ToString()逻辑。 - 异常捕获覆盖不全:现有代码只捕获了
ArgumentOutOfRangeException(选中行不存在的场景),没有覆盖类型转换、数据库查询类异常,出错时无法给出有效提示,也无法在出错时恢复主窗体显示状态。 - 额外高危隐患:直接拼接变量到SQL语句存在SQL注入风险,若
codusu为字符串类型,未加单引号也会直接导致查询失败。
修复方案
- 修正
ativo字段的读取逻辑
不要直接用Field<char>读取该字段,先按字符串类型取值,做非空判断后再取首字符,替换原代码中对应的传参行:// 替换原来的 dt.Rows[0].Field<Char>("ativo") string ativoVal = dt.Rows[0].Field<string>("ativo")?.Trim() ?? "N"; char ativo = ativoVal[0]; - 补全异常捕获逻辑
在现有catch块后增加通用异常捕获,覆盖类型转换、数据库操作类错误,同时出错时恢复主窗体显示:catch (Exception ex) { MessageBox.Show($"数据加载失败:{ex.Message}", "操作错误"); this.Show(); return; } - (强烈建议)改用参数化查询
替换现有SQL拼接写法,避免注入风险和语法错误,需要调整Connection.select方法支持参数传入,示例写法:// 废弃原拼接SQL写法 // dt = Connection.select($@"select * from view_coa_cadusu where codusu = {codusu}"); // 替换为参数化写法,参数传参格式可根据自己封装的Connection类调整 var parameters = new Dictionary<string, object> { {"codusu", codusu} }; dt = Connection.select("select * from view_coa_cadusu where codusu = @codusu", parameters);
内容的提问来源于stack exchange,提问作者Gustavo Megazorde
相关产品推荐
相关产品推荐

