检测和解决冲突

如果在即时模式下处理 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,可确保 UnderlyingValue 属性将包含数据库中的(冲突)值,Value 属性将保留用户输入的值,并且 OriginalValue 属性将保留字段的原始值(上次成功进行 UpdateBatch 调用之前具有的值)。 然后,可以使用这些值以编程方式解决冲突,或要求用户选择要使用的值。

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

'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  

可以使用当前 Record 或特定 Field 的 Status 属性来确定发生的是哪类冲突。

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

另请参阅

批处理模式