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

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);
    }
}

额外优化建议

  1. 重构SendSMS方法:把手机号作为参数传递进去,不要依赖全局变量numbers,避免循环中变量被覆盖的问题,代码更健壮。
  2. 添加事务支持:用SqlTransaction包裹所有插入操作,确保要么所有学生的考勤都保存成功,要么全部回滚,避免部分数据插入的异常情况。
  3. 日期参数优化:直接传递DateTime.Today给SQL参数,不需要转成字符串,SQL Server可以直接处理DateTime类型,避免日期格式兼容问题。
  4. 批量插入优化:如果学生数量较多,推荐使用SqlBulkCopy进行批量插入,比循环执行单条Insert效率提升明显。

内容的提问来源于stack exchange,提问作者Prateek Jahagirdar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:45:15