Parameters.AddWithValue使用运算符报username非空错误解决办法
问题原因
你遇到的username must be not null报错和Parameters.AddWithValue使用运算符无关,核心是代码存在3个明显错误:
- SQL语法逻辑错误:你写的
UNION查询仅在第二个admin_accounts表查询后加了where过滤,前半段查询accounts表时没有任何筛选条件,会把accounts表全量数据都查出来,完全没有做用户名/邮箱的匹配。 - 空值未处理:你注释掉了
username的入参空校验,当前端仅传邮箱参数做登录时,username变量为null,你直接把null值传入AddWithValue,后续设置Session时又直接使用这个null的username变量,就会触发非空报错。 - 校验逻辑顺序错误:你把密码校验放在
SqlDataReader的遍历循环里,只要读到的第一条记录密码不匹配就直接返回wrong_password,哪怕后续存在真正匹配的账号也不会继续校验,逻辑完全不成立。
正确实现方式
Parameters.AddWithValue本身只负责给SQL参数传值,不存在“在其中使用运算符”的用法,运算符需要写在SQL语句里,你只需要按以下步骤修正代码即可:
- 修正SQL语句,给两个关联表的查询都加上相同的用户名/邮箱匹配条件,使用
UNION ALL替代UNION提升查询性能 - 给参数传值时处理空值,入参为null时传入
DBNull.Value,避免SQL参数异常 - 调整校验逻辑,先确认查到匹配用户名/邮箱的账号记录,再做密码哈希比对、账号状态校验
- 写入Session时使用数据库查询返回的
username字段值,不要直接使用前端传入的参数,避免空值问题 - 移除冗余的手动
con.Close()调用和重复的using(con)嵌套,外层using代码块会自动释放数据库连接资源
修正后的核心代码
public JsonResult Loginzxc(string email, string username, string password) { // 基础入参校验 if (string.IsNullOrEmpty(password)) { return Json("wrong_password"); } if (string.IsNullOrEmpty(username) && string.IsNullOrEmpty(email)) { return Json("wrong_username"); } using (SqlConnection con = new SqlConnection(constring)) { string access = @"select reg_no, username, status, user_type, password from accounts where (@user is not null and username = @user) or (@email is not null and email = @email) union all select reg_no, username, status, user_type, password from admin_accounts where (@user is not null and username = @user) or (@email is not null and email = @email)"; string pass1 = ComputeSha256Hash(password); using (SqlCommand com = new SqlCommand(access, con)) { // 处理参数空值 com.Parameters.AddWithValue("@user", string.IsNullOrEmpty(username) ? (object)DBNull.Value : username); com.Parameters.AddWithValue("@email", string.IsNullOrEmpty(email) ? (object)DBNull.Value : email); com.CommandTimeout = 60; con.Open(); using (SqlDataReader rreader = com.ExecuteReader()) { if (rreader.HasRows) { while (rreader.Read()) { string pass2 = rreader["password"].ToString(); // 密码不匹配跳过当前记录,继续查找匹配账号 if (pass2 != pass1) { continue; } string status = rreader["status"].ToString(); string user_type = rreader["user_type"].ToString(); string reg_no = rreader["reg_no"].ToString(); // 使用数据库查询返回的用户名,不直接用前端传参 string dbUsername = rreader["username"].ToString(); if (user_type == "0") { user_type = "ADMIN"; } else if (user_type == "1") { user_type = "MEMBER"; } if (status == "INACTIVE") { return Json("INACTIVE"); } else if (status == "BLOCKED") { return Json("BLOCKED"); } else if (status == "ACTIVE") { HttpContext.Session.SetString("username", dbUsername); HttpContext.Session.SetString("reg_no", reg_no); HttpContext.Session.SetString("user_type", user_type); if (user_type == "ADMIN") { return Json(new { redirectUrl = Url.Action("Index", "Admin", new { id = "DSA" + reg_no }) }); } else if (user_type == "MEMBER") { return Json(new { redirectUrl = Url.Action("Index", "Home", new { id = "DSA" + reg_no }) }); } } else { return Json("error"); } } // 所有匹配账号遍历完成,密码均不匹配 return Json("wrong_password"); } } ViewBag.notfound = "login"; return Json("no record"); } } }
生产环境建议显式指定SqlParameter的类型和长度,不要完全依赖
AddWithValue的自动类型推断,避免出现执行计划缓存复用差、隐式类型转换导致索引失效的问题。
内容的提问来源于stack exchange,提问作者Syfy
相关产品推荐
相关产品推荐

