問題代碼
SqlDataReader sdr = comm.ExecuteReader();
if (sdr["id"] == null) { // 錯誤:未調用 Read(),且判斷邏輯反了
// 插入邏輯
}
錯誤分析
- 未調用
Read():ExecuteReader() 返回時指標在第一行之前,必須調用 Read() 推進到第一行
- 判斷邏輯反轉:用戶存在時
Read() 返回 true,不存在返回 false
- 並發風險:查詢與插入非原子操作,可能重複插入
修正代碼
string username = txtUserName.Text;
string password = txtPwd.Text;
string connStr = "server=127.0.0.1;Database=NetworkManagement;uid=sa;pwd=123456";
using (SqlConnection conn = new SqlConnection(connStr)) {
conn.Open();
// 1. 檢查用戶是否存在
string checkSql = "SELECT COUNT(1) FROM Administrator WHERE username = @user";
using (SqlCommand cmd = new SqlCommand(checkSql, conn)) {
cmd.Parameters.AddWithValue("@user", username);
int count = (int)cmd.ExecuteScalar();
if (count == 0) {
// 2. 不存在則插入(使用參數化防注入)
string insertSql = "INSERT INTO Administrator(username, password) VALUES (@user, @pwd)";
using (SqlCommand insertCmd = new SqlCommand(insertSql, conn)) {
insertCmd.Parameters.AddWithValue("@user", username);
insertCmd.Parameters.AddWithValue("@pwd", password); // 生產環境請存哈希值!
int rows = insertCmd.ExecuteNonQuery();
if (rows > 0) {
Session["name"] = username;
Response.Redirect("default.aspx");
} else {
Response.Redirect("error.aspx");
}
}
} else {
Response.Redirect("error.aspx"); // 用戶已存在
}
}
}
關鍵改進點
- 參數化查詢:防止 SQL 注入(原代碼直接拼接字符串極危險)
- ExecuteScalar:單值查詢最簡潔高效
- using 語句:自動釋放連接/命令/讀取器資源
- 密碼哈希:生產環境絕不可存明文,請用 BCrypt/Argon2
進階:原子化 UPSERT(SQL Server 2008+)
MERGE Administrator AS target
USING (SELECT @user AS username, @pwd AS password) AS source
ON target.username = source.username
WHEN NOT MATCHED THEN
INSERT (username, password) VALUES (source.username, source.password);