Поделиться через


Обнаружение и разрешение конфликтов

Если вы работаете с набором записей в немедленном режиме, существует гораздо меньше шансов на возникновение проблем с параллелизмом. С другой стороны, если ваше приложение использует режим пакетного обновления, есть большая вероятность того, что один пользователь изменит запись до того как изменения, сделанные другим пользователем, будут сохранены. В таком случае вы хотите, чтобы ваше приложение корректно обрабатывало конфликт. Возможно, вы хотите, чтобы последний пользователь отправил обновление на сервер "wins". Или вы можете позволить последнему пользователю решить, какое обновление должно иметь приоритет, предоставив ему выбор между двумя конфликтующими значениями.

В любом случае ADO предоставляет свойства UnderlyingValue и OriginalValue объекта Field для обработки этих типов конфликтов. Используйте эти свойства в сочетании с методом Resync и свойством Filter объекта Recordset.

Замечания

При возникновении конфликта ADO во время пакетного обновления в коллекцию ошибок будет добавлено предупреждение. Поэтому всегда следует проверять наличие ошибок сразу после вызова BatchUpdate и при их обнаружении начать тестирование предположения о возникновении конфликта. Первым шагом является задание свойства Filter в объекте Recordset, равным adFilterConflictingRecords. Это ограничивает представление набора записей только теми записями, которые находятся в конфликте. Если свойство RecordCount равно нулю после этого шага, вы знаете, что ошибка была вызвана чем-то, кроме конфликта.

При вызове BatchUpdate ADO и поставщика создают инструкции SQL для выполнения обновлений в источнике данных. Помните, что некоторые источники данных имеют ограничения на то, какие типы столбцов можно использовать в предложении WHERE.

Затем вызовите метод Resync в наборе записей с аргументом AffectRecords равным adAffectGroup и аргументом ResyncValues равным adResyncUnderlyingValues. Метод Resync обновляет данные в текущем объекте Recordset из базовой базы данных. Используя adAffectGroup, вы гарантируете, что только записи, видимые с текущим параметром фильтра, то есть только конфликтующие записи, повторно синхронизируются с базой данных. Это может значительно изменить производительность, если вы работаете с большим набором записей. Задав аргумент ResyncValues в adResyncUnderlyingValues при вызове Resync, необходимо убедиться, что свойство UnderlyingValue будет содержать (конфликтующее) значение из базы данных, что свойство Value будет поддерживать значение, введенное пользователем, и что свойство OriginalValue будет содержать исходное значение поля (значение, которое было до последнего успешного вызова UpdateBatch). Затем эти значения можно использовать для решения конфликта программным способом или для выбора значения, которое будет использоваться пользователем.

Этот метод показан в следующем примере кода. Пример искусственно создает конфликт с помощью отдельного набора записей для изменения значения в базовой таблице перед вызовом 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  

Вы можете использовать свойство Status текущей записи или определенного поля, чтобы определить, какой тип конфликта произошел.

Подробные сведения об обработке ошибок см. в Обработке Ошибок.

См. также

пакетный режим