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

DataTable合并异常:并发场景下合并后出现重复主键行求助

多用户环境下DataTable合并重复主键问题的解决

问题背景

在多用户应用中处理并发异常时,出现DataTable合并的意外行为:

  • 表单初始化时,通过SqlDataAdapter将Employees表数据填充到Tbl
  • 修改表单上某员工FirstName后,该员工的FirstName被其他用户在数据库中修改,关闭表单更新Tbl时触发并发异常
  • 从数据库获取该员工最新值填充到TmpTbl,调用Tbl.Merge(TmpTbl, True)期望覆盖更新Tbl对应记录,但合并后Tbl出现两条主键相同的记录

用户代码如下:

'Populate the data displayed on the form.
CN.ConnectionString = ConnStr()
DA.SelectCommand = New SqlCommand("Select ID, LastName, FirstName, MidInit From Employees Where Deleted Is Null And TermDate Is Null Order By " &
                                          "LastName, FirstName, MidInit", CN)
DA.Fill(Tbl)
'Get values currently in the underlying database.
Dim TmpTbl As New DataTable
Dim DAA As New SqlDataAdapter
DAA.SelectCommand = New SqlCommand("Select ID, LastName, FirstName, MidInit From Employees Where ID = " & DBCX.Row.Item("ID"), CN)
DAA.Fill(TmpTbl)
'Merge the two tables.
Tbl.Merge(TmpTbl, True)

问题原因

当Tbl中目标记录处于DataRowState.Modified状态(即表单修改后未提交)时,Merge方法会保留原修改行,同时将TmpTbl中的新记录作为"新行"添加——因为Merge默认会区分行的状态和版本,不会直接覆盖处于修改状态的行,最终导致主键重复。

解决办法

方法1:重置目标行状态后合并

先将Tbl中对应记录的行状态重置为Unchanged,再执行Merge,这样Merge会用TmpTbl的最新值覆盖该行:

' 定位Tbl中需要更新的目标行
Dim targetRow As DataRow = Tbl.Rows.Find(DBCX.Row.Item("ID"))
If targetRow IsNot Nothing Then
    ' 重置行状态为Unchanged,放弃表单端的未提交修改
    targetRow.AcceptChanges()
End If
' 执行合并操作
Tbl.Merge(TmpTbl, True)

方法2:手动更新目标行字段

直接用TmpTbl的字段值覆盖Tbl对应行,避免Merge的状态判断逻辑影响:

If TmpTbl.Rows.Count > 0 Then
    Dim sourceRow As DataRow = TmpTbl.Rows(0)
    Dim targetRow As DataRow = Tbl.Rows.Find(sourceRow("ID"))
    If targetRow IsNot Nothing Then
        ' 逐个更新字段值
        targetRow("LastName") = sourceRow("LastName")
        targetRow("FirstName") = sourceRow("FirstName")
        targetRow("MidInit") = sourceRow("MidInit")
        ' 若需标记为已提交,可调用AcceptChanges()
        ' targetRow.AcceptChanges()
    End If
End If

方法3:确认主键设置正确性

确保Tbl和TmpTbl都正确设置主键列,避免Merge无法匹配同主键记录:

' 为Tbl设置主键
Tbl.PrimaryKey = New DataColumn() {Tbl.Columns("ID")}
' 为TmpTbl设置主键
TmpTbl.PrimaryKey = New DataColumn() {TmpTbl.Columns("ID")}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 05:07:47