如何检测并防止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 -->" 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:完善循环逻辑(快速修复)
修改事件代码,做两个关键处理:
- 找到当前触发事件的行,遍历时候跳过它
- 对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:从数据源校验(更可靠)
依赖页面控件校验容易受行状态影响,更稳妥的方式是直接从数据库查询校验,不依赖页面控件:
- 编写辅助方法查询重复:
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
- 在下拉框变更事件中调用:
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
相关产品推荐
相关产品推荐

