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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.11 16:16:23