检测和解决冲突

如果在即时模式下处理 Recordset,则出现并发问题的可能性要少得多。 另一方面,如果应用程序使用批处理模式更新,则一个用户可能会更改记录,然后再保存另一个用户编辑同一记录的更改。 在这种情况下,你希望应用程序能够正常处理冲突。 你可能希望最后一个向服务器发送更新的人“获胜”。或者,你可能想让最近的用户通过为他提供选择来决定哪个更新应优先,在两个冲突值之间进行取舍。

不管怎样,ADO 都会提供 Field 对象的 UnderlyingValue 和 OriginalValue 属性来处理这些类型的冲突。 将这些属性与 Recordset 的 Resync 方法和 Filter 属性结合使用。

言论

当 ADO 在批处理更新期间遇到冲突时,将向 Errors 集合中添加警告。 因此,在调用 BatchUpdate 后,应始终立即检查错误,如果发现错误,请开始测试遇到冲突的假设。 第一步是设置 Recordset 上的 Filter 属性等于 adFilterConflictingRecords。 这会将 Recordset 上的视图限制为仅存在冲突的记录。 如果此步骤后 RecordCount 属性等于零,则你知道错误是由冲突以外的其他内容引发的。

调用 BatchUpdate 时,ADO 和提供程序将生成 SQL 语句以对数据源执行更新。 请记住,某些数据源对 WHERE 子句中可以使用哪些类型的列有限制。

接下来,在 Recordset 上调用 Resync 方法,将 AffectRecords 参数设置为等于 adAffectGroup,将 ResyncValues 参数设置为等于 adResyncUnderlyingValues。 Resync 方法从基础数据库更新当前 Recordset 对象中的数据。 通过使用 adAffectGroup,可以确保只有与当前筛选器设置(即仅冲突记录)可见的记录与数据库重新同步。 处理大型记录集(Recordset)时,这可能会导致显著的性能差异。 通过在调用 Resync 时将 ResyncValues 参数设置为 adResyncUnderlyingValues,可确保基础值属性将包含数据库中的 (冲突) 值,Value 属性将保留用户输入的值,并且 OriginalValue 属性将保留字段的原始值(上次成功调用 UpdateBatch 调用之前的值)。 然后,可以使用这些值以编程方式解决冲突,或要求用户选择将使用的值。

下面的代码示例演示了此方法。 在调用 UpdateBatch 之前,该示例使用单独的 Recordset 更改基础表中的值,从而人为地创建冲突。

'BeginConflicts  
    strConn = "Provider=SQLOLEDB;Initial Catalog=Northwind;" & _  
              "Data Source=MySQLServer;Integrated Security=SSPI;"  
  
    strSQL = "SELECT * FROM Shippers WHERE ShipperID = 2"  
  
    'Open Rs and change a value  
    Set objRs1 = New ADODB.Recordset  
    Set objRs2 = New ADODB.Recordset  
    objRs1.CursorLocation = adUseClient  
    objRs1.Open strSQL, strConn, adOpenStatic, adLockBatchOptimistic, adCmdText  
    objRs1("Phone") = "(111) 555-1111"  
  
    'Introduce a conflict at the db...  
    objRs2.Open strSQL, strConn, adOpenKeyset, adLockOptimistic, adCmdText  
    objRs2("Phone") = "(999) 555-9999"  
    objRs2.Update  
    objRs2.Close  
    Set objRs2 = Nothing  
  
    On Error Resume Next  
    objRs1.UpdateBatch  
  
    If objRs1.ActiveConnection.Errors.Count <> 0 Then  
        Dim intConflicts As Integer  
  
        intConflicts = 0  
  
        objRs1.Filter = adFilterConflictingRecords  
  
        intConflicts = objRs1.RecordCount  
  
        'Resync so we can see the UnderlyingValue and offer user a choice.  
        'This sample only displays all three values and resets to original.  
        objRs1.Resync adAffectGroup, adResyncUnderlyingValues  
  
        If intConflicts > 0 Then  
            strMsg = "A conflict occurred with updates for " & intConflicts & _  
                     " record(s)." & vbCrLf & "The values will be restored" & _  
                     " to their original values." & vbCrLf & vbCrLf  
  
            objRs1.MoveFirst  
            While Not objRs1.EOF  
              strMsg = strMsg & "SHIPPER = " & objRs1("CompanyName") & vbCrLf  
              strMsg = strMsg & "Value = " & objRs1("Phone").Value & vbCrLf  
              strMsg = strMsg & "UnderlyingValue = " & _  
                                 objRs1("Phone").UnderlyingValue & vbCrLf  
              strMsg = strMsg & "OriginalValue = " & _  
                                 objRs1("Phone").OriginalValue & vbCrLf  
              strMsg = strMsg & vbCrLf & "Original value has been restored."  
  
              MsgBox strMsg, vbOKOnly, _  
                    "Conflict " & objRs1.AbsolutePosition & _  
                    " of " & intConflicts  
  
              objRs1("Phone").Value = objRs1("Phone").OriginalValue  
              objRs1.MoveNext  
            Wend  
  
            objRs1.UpdateBatch adAffectGroup  
        Else  
            'Other error occurred. Minimal handling in this example.  
             strMsg = "Errors occurred during the update. " & _  
                        objRs1.ActiveConnection.Errors(0).Number & " " & _  
                        objRs1.ActiveConnection.Errors(0).Description  
        End If  
  
        On Error GoTo 0  
    End If  
  
    objRs1.MoveFirst  
    objRs1.Close  
    Set objRs1 = Nothing  
'EndConflicts  

可以使用当前记录或特定字段的 Status 属性来确定发生冲突的类型。

有关错误处理的详细信息,请参阅 错误处理

另请参阅

批处理模式