ASP.NET GridView发送SMS异常:单个缺勤学生收到多条重复短信
问题排查与修复方案
嘿,我一眼就看到问题出在哪了——你的短信发送逻辑被放在了遍历GridView每一行的循环里面!这就导致每处理一个学生的考勤记录,就会查询一次当天所有缺勤学生并群发一次短信。假设你的GridView有50个学生,那单个缺勤学生就会收到50条重复短信,完全符合你描述的情况。
核心问题拆解
- 你在
foreach (GridViewRow row in gvStudents.Rows)的循环内部,每插入一条考勤记录成功后,就执行了一次select mobile from studentattendance...查询并调用SendSMS()。 - 循环执行多少次,这个发短信的逻辑就会跑多少次,每次都会把当天所有缺勤学生(包括刚标记的那一个)都发一遍短信,重复次数等于GridView的行数。
- 另外代码还存在SQL注入风险(直接拼接日期字符串)和数据库连接重复开关的低效问题,一并帮你修复了。
修复后的完整代码
我们需要把短信发送逻辑移到循环外面,等所有学生的考勤记录都插入完成后,再统一查询一次缺勤名单发送短信,同时优化数据库操作:
protected void InsertAttendence() { DateTime systemdate2 = DateTime.Today.Date; string constring = ConfigurationManager.ConnectionStrings["stjosephconnect"].ConnectionString; bool saveSuccess = true; // 第一步:先批量处理所有学生的考勤插入 using (SqlConnection conInsert = new SqlConnection(constring)) { conInsert.Open(); // 提前定义SQL语句,避免循环内重复创建 string insertQuery = @"INSERT INTO studentattendance( attdate, stsno, name, mobile, class, div, attendance, remarks, status, rem) VALUES( @attdate, @stsno, @name, @mobile, @class, @div, @attendance, @remarks, @status, @rem)"; foreach (GridViewRow row in gvStudents.Rows) { if (row.RowType != DataControlRowType.DataRow) continue; CheckBox chkAttendance = row.FindControl("chkAttendence") as CheckBox; string attendanceStatus = chkAttendance.Checked ? "Present" : "Absent"; // 简化控件获取写法 string Class = ((Label)row.FindControl("lblclass")).Text.Trim(); string StudentName = ((Label)row.FindControl("lblname")).Text.Trim(); string STSNO = ((Label)row.FindControl("lblstsno")).Text.Trim(); string Mobile = ((Label)row.FindControl("lblmobile")).Text.Trim(); string Division = ((Label)row.FindControl("lbldiv")).Text.Trim(); using (SqlCommand cmd = new SqlCommand(insertQuery, conInsert)) { // 清空参数,防止循环中参数重复绑定 cmd.Parameters.Clear(); cmd.Parameters.AddWithValue("@attdate", systemdate2); cmd.Parameters.AddWithValue("@stsno", STSNO); cmd.Parameters.AddWithValue("@name", StudentName); cmd.Parameters.AddWithValue("@mobile", Mobile); cmd.Parameters.AddWithValue("@class", Class); cmd.Parameters.AddWithValue("@div", Division); cmd.Parameters.AddWithValue("@attendance", attendanceStatus); cmd.Parameters.AddWithValue("@remarks", "Test Remarks!"); cmd.Parameters.AddWithValue("@status", "1"); cmd.Parameters.AddWithValue("@rem", "0"); try { int affectedRows = cmd.ExecuteNonQuery(); if (affectedRows <= 0) { saveSuccess = false; } } catch (Exception ex) { saveSuccess = false; Response.Write($"<script language='javascript'>alert('{Server.HtmlEncode(ex.Message)}')</script>"); } } } conInsert.Close(); } // 第二步:所有考勤记录插入完成后,统一发送缺勤短信 if (saveSuccess) { ScriptManager.RegisterStartupScript(this, this.GetType(), "saveSuccess", "alert('Data saved successfully, Thank-You!');", true); using (SqlConnection absentCon = new SqlConnection(constring)) { // 使用参数化查询,彻底避免SQL注入 string absentQuery = @"select mobile from studentattendance where convert(varchar(10), attdate, 120) = @TodaysDate and attendance='Absent'"; absentCon.Open(); using (SqlCommand absentCmd = new SqlCommand(absentQuery, absentCon)) { absentCmd.Parameters.AddWithValue("@TodaysDate", DateTime.Now.ToString("yyyy-MM-dd")); using (SqlDataReader dr = absentCmd.ExecuteReader()) { while (dr.Read()) { string mobile = dr["mobile"].ToString().TrimStart('0'); // 建议修改SendSMS方法,把手机号作为参数传入,不要用全局变量 SendSMS(mobile); } } } absentCon.Close(); } } else { ScriptManager.RegisterStartupScript(this, this.GetType(), "saveError", "alert('There was some error saving attendance data');", true); } }
额外优化建议
- 重构SendSMS方法:把手机号作为参数传递进去,不要依赖全局变量
numbers,避免循环中变量被覆盖的问题,代码更健壮。 - 添加事务支持:用
SqlTransaction包裹所有插入操作,确保要么所有学生的考勤都保存成功,要么全部回滚,避免部分数据插入的异常情况。 - 日期参数优化:直接传递
DateTime.Today给SQL参数,不需要转成字符串,SQL Server可以直接处理DateTime类型,避免日期格式兼容问题。 - 批量插入优化:如果学生数量较多,推荐使用
SqlBulkCopy进行批量插入,比循环执行单条Insert效率提升明显。
内容的提问来源于stack exchange,提问作者Prateek Jahagirdar
相关产品推荐
相关产品推荐

