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

VB.NET DataGridView多行数据保存报索引超出范围错误

问题现象
  • 开发场景:基于VB.NET实现库存入库功能,需将DataGridView控件录入的商品数量数据写入MySQL数据库
  • 测试表现:单条行数据保存流程运行完全正常,录入多行数据触发保存操作时直接抛出运行时异常
  • 报错定位:异常触发位置为@pid参数传递代码行:
    .Parameters.AddWithValue("@pid", CInt(DataGridView1.Rows(i).Cells(1).Value.ToString))
    
  • 异常提示:保存多行GridView数据时,出现「索引超出范围。必须为非负值且小于集合大小」的错误
核心报错根因
  • 多行保存的循环逻辑For i As Integer = 0 To DataGridView1.Rows.Count - 1内部,在处理第一行数据时,代码直接执行了DataGridView1.Rows.Clear()清空整个DataGridView的行集合
  • 循环迭代到第二行时,i值增长为1,但此时DataGridView的行集合已被清空,集合大小为0,访问Rows(i)时自然触发索引越界异常
  • 单条数据保存时循环仅执行1次,不会出现清空后重复访问的逻辑,因此表现正常
完整保存逻辑代码
Private Sub BtnSave_Click(sender As Object, e As EventArgs) Handles BtnSave.Click
    TxtReferenceNo.Text.Trim()
    TxtReceiveBy.Text.Trim()

    For a As Integer = 0 To DataGridView1.Rows.Count - 1
        If DataGridView1.Rows(a).Cells(7).Value = String.Empty Then
            MsgBox("Quantity must not be empty", MsgBoxStyle.Critical, "")
            Exit Sub
        End If
    Next

    Dim sdate As String = DateTimeReceived.Value.ToString("yyyy-MM-dd")
    Dim d1, d2 As Date
    Dim found As Boolean
    d1 = sdate
    d2 = Now.ToShortDateString

    If d1 <> d2 Then
        MsgBox("Date should should be current date", MsgBoxStyle.Critical, "")
        Exit Sub
    End If

    For i As Integer = 0 To DataGridView1.Rows.Count - 1
        Dim Tsql As String
        Tsql = "select * from tblstockin where refno like '" & TxtReferenceNo.Text & "'"

        Con = New MySqlConnection
        With Con
            If .State = ConnectionState.Closed Then
                .ConnectionString = StrCon
                .Open()
            End If
        End With

        Comm = New MySqlCommand(Tsql, Con)
        dr = Comm.ExecuteReader
        dr.Read()
        If dr.HasRows Then
            found = True
        End If
        dr.Close()
        Con.Close()

        Try
            If Not (IsNumeric(DataGridView1.Rows(i).Cells(7).Value.ToString)) Then
                MsgBox("Quantity should be numeric", MsgBoxStyle.Critical, "")
                Exit Sub
            End If
        Catch ex As Exception

        End Try

        If found = False Then
            Dim sql As String
            sql = "insert into tblstockin (refno, receivedby, pid, qty, sdate) 
                  values (@refno, @receivedby, @pid, @qty, @sdate) "

            Con = New MySqlConnection
            With Con
                If .State = ConnectionState.Closed Then
                    .ConnectionString = StrCon
                    .Open()
                End If
            End With

            Comm = New MySqlCommand(sql, Con)
            With Comm
                .Parameters.Clear()
                .CommandText = sql
                .Parameters.AddWithValue("@refno", TxtReferenceNo.Text)
                .Parameters.AddWithValue("@receivedby", TxtReceiveBy.Text)
                .Parameters.AddWithValue("@pid", CInt(DataGridView1.Rows(i).Cells(1).Value.ToString))
                .Parameters.AddWithValue("@qty", CInt(DataGridView1.Rows(i).Cells(7).Value.ToString))
                .Parameters.AddWithValue("@sdate", sdate)
            End With

            If MessageBox.Show("Are you sure to save Stock", "", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.Yes Then
                Dim result As Integer = Comm.ExecuteNonQuery
                MsgBox("Stock has been saved", MsgBoxStyle.Information, "")
                TxtReceiveBy.Clear()
                TxtCount.Clear()
                Con.Close()

                Dim hsql As String
                hsql = " insert into tblgenid (codeid, code) values (@codeid, @code) "

                Con = New MySqlConnection
                With Con
                    If .State = ConnectionState.Closed Then
                        .ConnectionString = StrCon
                        .Open()
                    End If
                End With

                Comm = New MySqlCommand(hsql, Con)
                With Comm
                    .Parameters.Clear()
                    .CommandText = hsql
                    .Parameters.AddWithValue("@codeid", LblD.Text)
                    .Parameters.AddWithValue("@code", TxtReferenceNo.Text)
                End With
                Comm.ExecuteNonQuery()
                Con.Close()
                TxtReferenceNo.Clear()
            End If

            Dim rsql As String
            rsql = "update tblproduct set qty = qty + " & CInt(DataGridView1.Rows(i).Cells(7).Value.ToString) &
                " where id like '" & DataGridView1.Rows(i).Cells(1).Value.ToString & "'"

            Con = New MySqlConnection
            With Con
                If .State = ConnectionState.Closed Then
                    .ConnectionString = StrCon
                    .Open()
                End If
            End With

            Comm = New MySqlCommand(rsql, Con)
            With Comm
                .Parameters.Clear()
                .CommandText = rsql
            End With
            Comm.ExecuteNonQuery()
            DataGridView2.Rows.Clear()
            DataGridView1.Rows.Clear()
            CbxSearch.ResetText()
            TxtSearch.Clear()
            TxtReferenceNo.Clear()
            Con.Close()
        Else
            MsgBox("Reference no already exists", MsgBoxStyle.Critical, "")
        End If
    Next

    GenerateStock()

    With Frm_Product_List
        .LoadProductList()
    End With
End Sub
修复方向
  • 将DataGridView1.Rows.Clear()、控件清空、页面刷新等逻辑,移到整个多行循环执行完成后再运行,避免循环过程中清空行集合导致后续迭代访问越界
  • 将单号重复校验逻辑移到循环外部,仅校验一次当前录入的单据号是否存在即可,无需每一行重复查询数据库
  • 补充found变量的初始化逻辑,每次执行数据库查询前重置状态,避免上一次循环的查询结果影响当前行的判断
  • 优化数据库连接管理逻辑,不要在循环内反复创建、关闭连接,减少不必要的数据库开销
  • 补全SQL参数化逻辑,将商品数量、商品id等拼接在SQL语句中的值改为参数传递,规避SQL注入风险
  • 将保存确认弹窗逻辑移到循环外部,仅在所有行数据校验完成后弹出一次确认框,优化交互体验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 22:24:03