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

如何检测并防止GridView重复条目,解决空引用异常问题

解决GridView编辑行时重复检测的空引用异常

我是编程新手,在实现带多控件的GridView数据录入功能时,遇到了检测并防止重复输入的问题。目前用foreach循环遍历控件做检测,大部分场景正常,但编辑第一条记录并点击更新时,哪怕没改值也触发空引用异常。

GridView核心代码

<asp:GridView ID="GridView2" runat="server"
        EmptyDataText="No Miscellaneous" AutoGenerateColumns="False"
        ShowHeaderWhenEmpty="True" DataKeyNames="RecID"
        ShowFooter="True" DataSourceID="SqlDataSource7" Width="95%"
        OnRowDataBound="OnRowDataBound" >

    <EmptyDataTemplate>
        <asp:DropDownList ID="ddlMiscEquipmentNew" runat="server" DataSourceID="SqlDataSourcePartEquipment" DataValueField="RecID"
            DataTextField="EquipmentPart" AppendDataBoundItems="True" AutoPostBack="True" Width="220px" Height="20px" style="margin-left: 70px;" >
            <asp:ListItem Value="0">---SELECT---</asp:ListItem>
        </asp:DropDownList>
        <asp:DropDownList ID="ddlMiscNew" runat="server" DataSourceID="SqlDataSourceMisc" DataValueField="RecID" DataTextField="Description"
            AppendDataBoundItems="true" Width="220px" Height="20px" AutoPostBack="True" OnSelectedIndexChanged="ddlMiscNew_SelectedIndexChanged"
            OnDataBound="ddlMiscNew_DataBinding">
            <asp:ListItem Value="0">---SELECT---</asp:ListItem>
        </asp:DropDownList>
        <asp:TextBox ID="txtMiscQuantityNew" runat="server" Width="95px" Style="margin-left: 68px;" AutoPostBack="True" OnTextChanged="txtMiscQuantityNew_TextChanged" />
        <asp:TextBox ID="txtMiscCostNew" runat="server" Width="95px" Text="0" ReadOnly="True" />
        <asp:TextBox ID="txtMiscRequestedNew" runat="server" Width="95px" Text="0" ReadOnly="True" />
        <asp:TextBox ID="txtMiscTaxNew" runat="server" Width="95px" Text="0" />
        <asp:Button ID="InsertDetail" runat="server" CommandName="InsertDetail" Height="25px" Text="Add Detail" Width="85px" UseSubmitBehavior="True" />
    </EmptyDataTemplate>

    <AlternatingRowStyle BackColor="#CCCCCC" />
        <Columns>
            <asp:CommandField ShowEditButton="True" FooterText="Add --&gt;" ShowDeleteButton="true" HeaderStyle-Width="70px" />
            <asp:BoundField DataField="RecID" HeaderText="RecID" SortExpression="RecID" ReadOnly="True" Visible="False" />

            <asp:TemplateField HeaderText="Miscellaneous" ItemStyle-HorizontalAlign="center">
                <ItemTemplate>
                    <asp:Label ID="lblMiscEquipmentAdd" Text='<%# Bind("EquipmentType") %>' runat="server" Width="220px" />
                    <asp:Label ID="lblMiscDescriptionAdd" Text='<%# Bind("MiscDescription") %>' runat="server" Width="220px" />
                </ItemTemplate>
                <EditItemTemplate>
                    <asp:DropDownList ID="ddlMiscEquipmentEdit" runat="server" DataSourceID="SqlDataSourcePartEquipment" DataValueField="RecID" DataTextField="EquipmentPart" SelectedValue='<%# Eval("EquipmentID") %>'
                        AppendDataBoundItems="True" AutoPostBack="True" Width="220px" Height="20px">
                        <asp:ListItem Value="0">---SELECT---</asp:ListItem>
                    </asp:DropDownList>
                    <asp:DropDownList ID="ddlMiscEdit" runat="server" DataSourceID="SqlDataSourceMisc" DataValueField="RecID" DataTextField="Description" SelectedValue='<%# Eval("MiscType") %>' AppendDataBoundItems="True" Width="220px" Height="20px" AutoPostBack="True" OnSelectedIndexChanged="ddlMiscEdit_SelectedIndexChanged">
                        <asp:ListItem Value="0">---SELECT---</asp:ListItem>
                    </asp:DropDownList>
                </EditItemTemplate>
                <FooterTemplate>
                    <asp:DropDownList ID="ddlMiscEquipmentInsert" runat="server" DataSourceID="SqlDataSourcePartEquipment" DataValueField="RecID" DataTextField="EquipmentPart"
                        AppendDataBoundItems="True" Width="220px" Height="20px" style="margin-left: 29px;">
                        <asp:ListItem Value="0">---SELECT---</asp:ListItem>
                    </asp:DropDownList>
                    <asp:DropDownList ID="ddlMiscInsert" runat="server" DataSourceID="SqlDataSourceMisc" DataValueField="RecID" DataTextField="Description" AppendDataBoundItems="True" Width="220px" Height="20px" AutoPostBack="True" OnSelectedIndexChanged="ddlMiscInsert_SelectedIndexChanged">
                        <asp:ListItem Value="0">---SELECT---</asp:ListItem>
                    </asp:DropDownList>
                </FooterTemplate>
            </asp:TemplateField>
</asp:GridView>

问题核心与现有检测代码

问题出在EditItemTemplate里的ddlMiscEdit下拉框和ItemTemplate里的lblMiscDescriptionAdd标签:编辑第一行修改ddlMiscEdit的值时,会触发lblMiscDescriptionAdd的空引用异常。

现有重复检测代码(写在ddlMiscEdit_SelectedIndexChanged事件中):

For Each row In GridView2.Rows
        Dim miscdescription As Label = (CType(row.FindControl("lblMiscDescriptionAdd"), Label))
        If miscdescription.Text = ddlMiscEdit.SelectedItem.Text Then
            lblErrorMisc.Text = "Miscellaneous items can only be claimed once"
            Return
        Else
            lblErrorMisc.Text = ""
            Return
        End If
    Next

原因分析

你的推测完全正确:编辑模式下,当前行的ItemTemplate控件会被隐藏,替换为EditItemTemplate里的控件,所以遍历所有行时,正在编辑的那一行找不到lblMiscDescriptionAdd,直接访问Text属性就会抛出空引用异常。另外把循环放RowCommand的Update段时其他行出问题,是因为遍历逻辑没排除当前编辑行本身——编辑时允许保留原数值,不该和自己比较。

解决方案

方案1:完善循环逻辑(快速修复)

修改事件代码,做两个关键处理:

  1. 找到当前触发事件的行,遍历时候跳过它
  2. 对FindControl的结果做空值判断,避免空引用
Protected Sub ddlMiscEdit_SelectedIndexChanged(sender As Object, e As EventArgs)
    ' 获取当前触发事件的下拉框所在行
    Dim currentDdl As DropDownList = DirectCast(sender, DropDownList)
    Dim currentRow As GridViewRow = DirectCast(currentDdl.NamingContainer, GridViewRow)
    Dim selectedText As String = currentDdl.SelectedItem.Text

    lblErrorMisc.Text = "" ' 默认清空错误提示

    For Each row As GridViewRow In GridView2.Rows
        ' 跳过当前编辑行,避免和自己的原数值比较
        If row.RowIndex = currentRow.RowIndex Then
            Continue For
        End If

        ' 先判断控件是否存在,再访问Text属性
        Dim miscLabel As Label = DirectCast(row.FindControl("lblMiscDescriptionAdd"), Label)
        If miscLabel IsNot Nothing AndAlso miscLabel.Text = selectedText Then
            lblErrorMisc.Text = "Miscellaneous items can only be claimed once"
            Return
        End If
    Next
End Sub

方案2:从数据源校验(更可靠)

依赖页面控件校验容易受行状态影响,更稳妥的方式是直接从数据库查询校验,不依赖页面控件:

  1. 编写辅助方法查询重复:
Private Function IsMiscDuplicate(selectedDesc As String, excludeRecID As Integer) As Boolean
    ' 替换为你的数据库连接字符串
    Dim connStr As String = ConfigurationManager.ConnectionStrings("YourConnString").ConnectionString
    Using conn As New SqlConnection(connStr)
        Dim cmd As New SqlCommand("SELECT COUNT(*) FROM YourTableName WHERE MiscDescription = @Desc AND RecID != @ExcludeID", conn)
        cmd.Parameters.AddWithValue("@Desc", selectedDesc)
        cmd.Parameters.AddWithValue("@ExcludeID", excludeRecID)
        conn.Open()
        Return Convert.ToInt32(cmd.ExecuteScalar()) > 0
    End Using
End Function
  1. 在下拉框变更事件中调用:
Protected Sub ddlMiscEdit_SelectedIndexChanged(sender As Object, e As EventArgs)
    Dim currentDdl As DropDownList = DirectCast(sender, DropDownList)
    Dim currentRow As GridViewRow = DirectCast(currentDdl.NamingContainer, GridViewRow)
    ' 从DataKeys获取当前行的RecID,安全可靠
    Dim excludeID As Integer = Convert.ToInt32(GridView2.DataKeys(currentRow.RowIndex).Value)
    Dim selectedText As String = currentDdl.SelectedItem.Text

    If IsMiscDuplicate(selectedText, excludeID) Then
        lblErrorMisc.Text = "Miscellaneous items can only be claimed once"
    Else
        lblErrorMisc.Text = ""
    End If
End Sub

额外注意事项

  • 可以通过row.RowState = DataControlRowState.Edit判断是否为编辑行,实现跳过逻辑
  • 尽量避免依赖页面控件的文本做校验,直接和数据源交互更稳定
  • 任何FindControl操作后,都要先判断结果是否为Nothing,这是避免空引用异常的基础操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 19:55:22