WinForm校验数据库用户名密码登录时出现空引用错误求助
问题原因
你遇到的object reference not set to an instance object空引用异常,触发点非常明确:当输入的用户名或密码错误时,SQL查询匹配不到对应记录,cmd.ExecuteScalar()会返回null,你直接对null调用.ToString()方法,就会抛出这个异常。
除此之外你的代码还有3处逻辑错误:
- 重复查询:校验用户名、校验角色写了两次条件完全一致的SQL查询,属于不必要的性能浪费
- 角色判断逻辑恒成立:你写的判断条件
RoleId != "Administrator"|| RoleId != "Financial" || RoleId != "Financial"存在逻辑错误,不管RoleId是什么值,这个条件永远为真,哪怕输入正确的账号也会弹出密码错误提示 - 空值判断顺序错误:你先把查询结果转成字符串,再判断结果是否为null,这时候空引用异常已经触发,后续的判断代码根本不会执行
修复代码
把重复的校验逻辑合并,先判断查询结果是否为空再做类型转换,修正逻辑判断,同时优化数据库资源释放:
首先把原来拆分的两个校验方法合并成一个统一的登录校验方法:
// 全局存储当前登录用户角色 string PublicRole; private bool ValidateLogin() { // 先做表单非空校验 if (txtUsername.Text.Trim() == string.Empty || txtPassword.Text.Trim() == string.Empty) { MessageBox.Show("用户名和密码均为必填项", "请输入账号信息", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } // 一次查询直接拿到角色信息,不需要重复查库 const string loginSql = "SELECT Access FROM Userlogins WHERE Username=@user AND Credential=@pass"; // using块会自动释放SqlCommand资源,避免内存泄漏 using (SqlCommand cmd = new SqlCommand(loginSql, con)) { cmd.Parameters.AddWithValue("@user", txtUsername.Text.Trim()); cmd.Parameters.AddWithValue("@pass", txtPassword.Text.Trim()); // 先拿到查询结果,不要直接调用ToString() object queryResult = cmd.ExecuteScalar(); // 结果为null说明账号密码不匹配 if (queryResult == null) { MessageBox.Show("用户名或密码错误", "登录信息错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } PublicRole = queryResult.ToString(); // 校验角色是否在合法范围内 string[] allowedRoles = { "Administrator", "Secretary", "Financial" }; if (!allowedRoles.Contains(PublicRole)) { MessageBox.Show("当前账号角色无登录权限", "登录失败", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } } return true; }
然后简化登录按钮的点击事件:
private void BtnLogin_Click(object sender, EventArgs e) { try { if (ValidateLogin()) { // 用switch替代多个if判断,逻辑更清晰 switch (PublicRole) { case "Administrator": MessageBox.Show("管理员模式", "Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information); break; case "Secretary": MessageBox.Show("秘书模式", "Secretary", MessageBoxButtons.OK, MessageBoxIcon.Information); break; case "Financial": MessageBox.Show("财务模式", "Financial", MessageBoxButtons.OK, MessageBoxIcon.Information); break; } // 后续在这里写跳转对应功能页面的逻辑即可 } } catch (Exception ex) { MessageBox.Show($"程序运行出错:\n错误信息:{ex.Message}", "登录错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
优化建议
- 数据库连接
con建议也使用using块包裹,做到随用随开、用完即关,不要长期维持打开状态,避免连接池泄漏 - 你提到目前已经在学习安全相关内容,后续做密码哈希存储时,直接使用官方提供的PBKDF2、BCrypt等成熟方案,不要自己实现哈希算法
- 参数化查询的写法你已经用对了,这点保持住,可以完全避免SQL注入问题
内容的提问来源于stack exchange,提问作者Reuben Hale
相关产品推荐
相关产品推荐

