次の方法で共有


競合の検出および解決

即時モードで Recordset を扱う場合、コンカレンシーの問題が発生する可能性はかなり低くなります。 一方、アプリケーションにバッチ モード更新を使っている場合、あるユーザーがレコードを変更したときが、同じレコードを編集している他のユーザーが変更を保存する前である可能性が高くなります。 このような場合、アプリケーションで競合を適切に処理することをお勧めします。 最後にサーバーに更新を送信した個人を "勝ち" にすることができます。また、競合する 2 つの値の選択肢を提示し、最新のユーザーがどちらの更新を優先するかを判断できるようにすることもできます。

いずれの場合でも、ADO には、この種の競合を処理する Field オブジェクトの UnderlyingValue および OriginalValue プロパティがあります。 これらのプロパティは、Recordset の Resync メソッドと Filter プロパティと組み合わせて使います。

注釈

バッチ更新中に ADO で競合が発生すると、Errors コレクションに警告が追加されます。 そのため、BatchUpdate を呼び出した直後に必ずエラーを確認し、エラーが見つかったら、競合が発生したという前提でテストを開始してください。 最初の手順は、Recordset の Filter プロパティを adFilterConflictingRecords に設定することです。 これにより、Recordset のビューは、競合しているレコードのみに制限されます。 この手順の後、RecordCount プロパティが 0 になった場合、エラーは競合以外の原因で発生したことがわかります。

BatchUpdate を呼び出すと、ADO とプロバイダーにより、データ ソースに対して更新を実行する SQL ステートメントが生成されます。 一部のデータ ソースには、WHERE 句で使用できる列の種類に制限があることに注意してください。

次に、AffectRecords 引数を adAffectGroup に設定し、ResyncValues 引数を adResyncUnderlyingValues に設定して Resync メソッドを Recordset に対して呼び出します。 Resync メソッドを使って、基となるデータベースの現在の Recordset オブジェクトに含まれるデータを更新します。 adAffectGroup を使うと、現在のフィルター設定で表示されるレコードのみ、つまり競合するレコードのみを確実にデータベースと再同期することができます。 その結果、大規模な Recordset を扱う場合のパフォーマンスに大きな違いが出る可能性があります。 Resync を呼び出すときに ResyncValues 引数を adResyncUnderlyingValues に設定すると、UnderlyingValue プロパティにデータベースの (競合する) 値を含めること、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  

現在の Record または特定の Field の Status プロパティを使って、発生した競合の種類を判断できます。

エラー処理の詳細については、エラー処理に関する記事を参照してください。

参照

バッチ モード