偵測和解決衝突
如果您在立即模式中處理 Recordset,則發生並行問題的機會要少得多。 另一方面,如果您的應用程式使用批次模式更新,很可能當一位使用者在編輯記錄時,另一位使用者已經對相同的記錄進行變更,而後者的變更尚未被儲存。 在這種情況下,您會想要讓應用程式正常處理衝突。 您可能希望最後一個人傳送更新給伺服器「獲勝」。或者,您可能想要讓最近的用戶決定哪一個更新應該優先,方法是提供他兩個衝突值之間的選擇。
無論情況如何,ADO 都會提供 Field 物件的 UnderlyingValue 和 OriginalValue 屬性來處理這些衝突類型。 將這些屬性與 Recordset 的 Resync 方法和 Filter 屬性搭配使用。
備註
當 ADO 在批次更新期間發生衝突時,警告將會新增至 Errors 集合。 因此,您應該一律在呼叫 BatchUpdate 之後立即檢查錯誤,如果您發現錯誤,請開始測試您遇到衝突的假設。 第一個步驟是在 Recordset 上設定等於 adFilterConflictingRecords 的 Filter 屬性。 這會將 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 屬性來判斷發生何種衝突。
如需錯誤處理的詳細資訊,請參閱 錯誤處理。