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

DataGridViewRow单元格计数引发ArgumentOutOfRangeException问题排查

问题解决:ArgumentOutOfRangeException异常分析与修复

问题现象

运行代码时触发ArgumentOutOfRangeException异常,报错时DataGridViewRow.Cells.Count为15(即表格有15列),但表格仅有5行数据,程序在i=5时停止运行。

核心错误原因

代码存在变量混淆:外层foreach遍历DataGridView的每一行(rw),但内层for循环的变量i是用来遍历当前行的列索引,却被错误地当作dataGridView1.Rows的行索引使用。当i增长到5时,超过了表格的最大行索引(5行数据的索引范围是0-4),访问dataGridView1.Rows[5]直接触发越界异常。

同时代码还存在其他潜在问题:

  • 逻辑判断错误:if (rw.Cells[i].Value != null || rw.Cells[i].Value != DBNull.Value || !String.IsNullOrEmpty(rw.Cells[i].Value.ToString())) 用||会导致条件永远为true,应该改为&&,否则只要有一个条件满足就会执行分支,比如Value为DBNull时,调用ToString()会直接报错。
  • SQL注入风险:直接拼接SQL语句,未使用参数化查询,存在安全隐患且易因特殊字符导致语法错误。
  • 数据库连接管理混乱:循环中频繁打开关闭连接,部分分支关闭连接后可能重复执行关闭操作。
  • 数据转换无安全校验:直接使用Convert.ToDouble、Convert.ToDateTime,单元格值无效时会触发异常。

修复步骤

  1. 修正行索引引用:将所有dataGridView1.Rows[i]替换为当前遍历的行rw,业务逻辑是处理整行数据,不需要用列索引访问行。
  2. 修复逻辑判断条件:把||改为&&,确保单元格值不为null、不为DBNull,且转换为字符串后不为空。
  3. 优化连接管理:用using语句自动管理数据库连接,避免手动关闭的混乱。
  4. 替换参数化SQL:消除SQL注入风险,提升代码稳定性。
  5. 添加安全转换:使用TryParse系列方法进行数据转换,防止无效值引发异常。

修正后的代码示例

// 用using自动管理数据库连接,避免手动关闭的问题
using (SqlConnection commcon = new SqlConnection("你的数据库连接字符串"))
{
    commcon.Open();
    foreach (DataGridViewRow rw in this.dataGridView1.Rows)
    {
        // 跳过DataGridView自动生成的空行
        if (rw.IsNewRow) continue;

        object accountCellValue = rw.Cells[1].Value;
        // 正确的非空判断逻辑
        if (accountCellValue != null && accountCellValue != DBNull.Value && !string.IsNullOrEmpty(accountCellValue.ToString()))
        {
            int obaccountid = 0;
            double obtotal = 0;

            int billno = 0;
            int accountid = 0;
            string accountno = accountCellValue.ToString();
            string accountname = "";
            double total = 0;
            int cellnumber = 0;
            DateTime colldate;

            // 安全转换账号ID
            if (!int.TryParse(getAccountID(accountno), out accountid))
            {
                MessageBox.Show($"账号{accountno}格式无效", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                continue;
            }

            billno = getBillno(accountid);
            accountname = getProperAccountName(accountid);

            // 安全转换金额
            if (!double.TryParse(rw.Cells[3].Value?.ToString(), out total))
            {
                MessageBox.Show($"第{rw.Index+1}行金额格式错误", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                continue;
            }

            // 安全转换手机号
            if (!int.TryParse(rw.Cells[14].Value?.ToString(), out cellnumber))
            {
                MessageBox.Show($"第{rw.Index+1}行手机号格式错误", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                continue;
            }

            // 安全转换日期
            if (!DateTime.TryParse(rw.Cells[0].Value?.ToString(), out colldate))
            {
                MessageBox.Show($"第{rw.Index+1}行日期格式错误", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                continue;
            }

            // 参数化SQL,避免注入风险
            string sql = @"SELECT TOP 1 total, accountid 
                           FROM [bicos-ob].dbo.[Outstanding Bills] 
                           WHERE accountid = @AccountId 
                             AND MONTH([billing date]) = MONTH(@CollDate) 
                             AND YEAR([billing date]) = YEAR(@CollDate) 
                           ORDER BY id desc";
            using (SqlDataAdapter a = new SqlDataAdapter(sql, commcon))
            {
                a.SelectCommand.Parameters.AddWithValue("@AccountId", accountid);
                a.SelectCommand.Parameters.AddWithValue("@CollDate", colldate);

                DataTable t = new DataTable();
                a.Fill(t);
                if (t.Rows.Count > 0)  
                {
                    // 安全转换未结账单数据
                    if (!double.TryParse(t.Rows[0]["total"].ToString(), out obtotal))
                    {
                        MessageBox.Show("未结账单金额格式错误", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        continue;
                    }
                    if (!int.TryParse(t.Rows[0]["accountid"].ToString(), out obaccountid))
                    {
                        MessageBox.Show("未结账单账号ID格式错误", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        continue;
                    }

                    // 执行对应业务逻辑
                    if (obtotal == total && obaccountid == accountid)
                    {
                        using (SqlCommand cmd = new SqlCommand("[save__collection_RDPayments]", commcon))
                        {
                            cmd.CommandType = CommandType.StoredProcedure;
                            cmd.Parameters.AddWithValue("@billno", billno);
                            cmd.Parameters.AddWithValue("@accountid", accountid);
                            cmd.Parameters.AddWithValue("@accountno", accountno);
                            cmd.Parameters.AddWithValue("@accountname", accountname);
                            cmd.Parameters.AddWithValue("@or", ORNumber);
                            cmd.Parameters.AddWithValue("@collectiondate", colldate);
                            cmd.Parameters.AddWithValue("@paymenttype", paymenttype);
                            cmd.Parameters.AddWithValue("@checkno", checkno);
                            cmd.Parameters.AddWithValue("@overpayment", 0);
                            cmd.Parameters.AddWithValue("@partialpayment", 0);
                            cmd.Parameters.AddWithValue("@userid", getUserID(user));
                            cmd.Parameters.AddWithValue("@RefNo", rw.Cells[7].Value?.ToString() ?? "");
                            cmd.ExecuteNonQuery();
                        }

                        appendFiles($"已入账款项: {accountno} {accountid} {accountname}");
                        InsertAuditLog(commcon, colldate, accountno, accountname, "已入账", cellnumber);
                    }
                    else if (obtotal < total && obaccountid == accountid)
                    {
                        InsertAuditLog(commcon, colldate, accountno, accountname, "超额付款", cellnumber);
                    }
                    else if (obtotal > total && obaccountid == accountid)
                    {
                        InsertAuditLog(commcon, colldate, accountno, accountname, "付款金额小于账单金额", cellnumber);
                    }
                    else if (obaccountid != accountid)
                    {
                        InsertAuditLog(commcon, colldate, accountno, accountname, "账号不匹配", cellnumber);
                    }
                    else
                    {
                        InsertAuditLog(commcon, colldate, accountno, accountname, "未知状态", cellnumber);
                    }
                }
                else
                {
                    MessageBox.Show($"第{rw.Index+1}行未找到对应未结账单数据!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }
        else
        {
            MessageBox.Show($"第{rw.Index+1}行账号不能为空!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}

// 提取审计日志插入逻辑,减少代码重复
private void InsertAuditLog(SqlConnection conn, DateTime collDate, string accountNo, string accountName, string remarks, int cellNumber)
{
    string sql = @"INSERT INTO CodeAventus_ePostingPortal.dbo.[audit_cr_log] 
                   (collectiondate, accountno, accountname, remarks, transactiondate, cellphonenumber) 
                   VALUES(@collectiondate, @accountno, @accountname, @remarks, @transactiondate, @cellphonenumber)";
    using (SqlCommand cmdlog = new SqlCommand(sql, conn))
    {
        cmdlog.Parameters.AddWithValue("@collectiondate", collDate);
        cmdlog.Parameters.AddWithValue("@accountno", accountNo);
        cmdlog.Parameters.AddWithValue("@accountname", accountName);
        cmdlog.Parameters.AddWithValue("@remarks", remarks);
        cmdlog.Parameters.AddWithValue("@transactiondate", DateTime.Now);
        cmdlog.Parameters.AddWithValue("@cellphonenumber", cellNumber);
        cmdlog.ExecuteNonQuery();
    }
}

额外说明

  • 原代码内层for循环遍历列是错误逻辑,业务是处理整行数据,因此直接移除该循环,改为处理当前行rw。
  • 添加rw.IsNewRow判断,跳过DataGridView自动生成的空行,避免处理无效数据。
  • 提取InsertAuditLog方法,减少重复代码,提升可维护性。
  • 所有数据转换使用TryParse,确保代码不会因无效值崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 23:50:37