Share via


Detecting and Resolving Conflicts

During data update operations, especially in shared environments, you might want to determine which fields have changed or what the original or the current values are in changed fields. Visual FoxPro buffering and the GETFLDSTATE( ), GETNEXTMODIFIED( ), OLDVAL( ) and CURVAL( ) functions, enable you to determine which field has changed, find the changed data, and compare the current, original, and edited values so you can decide how to handle an error or conflict.

To detect a change in a field

GETFLDSTATE( ) works on unbuffered data; however, this function is even more effective when you've enabled record buffering. For instance, use GETFLDSTATE( ) in the code of a Skip button on a form. When you move the record pointer, Visual FoxPro checks the status of all fields in the record as in the following example:

lModified = .F.
FOR nFieldNum = 1 TO FCOUNT( ) && Check all fields 
   if GETFLDSTATE(nFieldNum) = 2  && Modified
      lModified = .T.
      EXIT && Insert update/Save routine here.
   ENDIF && See the next example
ENDFOR

To detect and locate a changed record in buffered data

GETNEXTMODIFIED( ), with zero as a parameter, finds the first modified record. If another user makes changes to the buffered table, any changes encountered by a TABLEUPDATE( ) command in your buffer will cause conflicts. You can evaluate the conflicting values and resolve them using the CURVAL( ), OLDVAL( ), and MESSAGEBOX( ) functions. CURVAL( ) returns the current value of the record on disk, while OLDVAL( ) returns the value of the record at the time it was buffered.

To determine the original value of a buffered field

OLDVAL( ) returns the value of a buffered field.

To determine the current value of a buffered field on disk

CURVAL( ) returns the current value on disk of a buffered field before any edits were performed.

You can create an error-handling procedure that compares the current and original values, enabling you to determine whether to commit the current change or to accept an earlier change to data in a shared environment.

The following example uses GETNEXTMODIFIED( ), CURVAL( ), and OLDVAL( ) to provide the user with an informed choice in an update operation. This example continues from detection of the first modified record and might be contained in an Update or Save button on a form.

Click Event Code for an Update or Save Button

Code Comment
   nCurRec = GETNEXTMODIFIED(nCurRec)
   DO WHILE nCurRec <> 0
   GO nCurRec
   RLOCK( )
Loop through buffer.

Lock the modified record.
   FOR nField = 1 TO FCOUNT(cAlias)   
      cField = FIELD(nField)
      IF OLDVAL(cField) <> CURVAL(cField)
         nResult = MESSAGEBOX("Data was ;
            changed by another user. ;
            Keep changes?", 4+48+0, ;
            "Modified Record")
Look for conflict.

Compare the original value to the current value on the disk, and then ask the user what to do about the conflict.
         IF nResult = 7   
            TABLEREVERT(.F.)   
            UNLOCK RECORD nCurRec   
         ENDIF
      ENDIF
   ENDFOR
   nCurRec = GETNEXTMODIFIED(nCurRec)
ENDDO
If the user selects "No," revert this record, and then remove the lock.




Find the next modified record.
TABLEUPDATE(.T., .T.)   
Force update to all records.

Detecting Conflicts using Memo Fields

You can use the CompareMemo property to control when memo fields are used to detect update conflicts. This view and cursor property determines whether memo fields (types M or G) are included in the update WHERE clause. The default setting, True (.T.), means that memo fields are included in the WHERE clause. If you set this property to False (.F), memo fields don't participate in the update WHERE clause, regardless of the settings of UpdateType.

Optimistic conflict detection on Memo fields is disabled when CompareMemo is set to False. For conflict detection on memo values, set CompareMemo to True (.T.).

Rules for Managing Conflicts

Managing conflicts encountered in multiuser environments can require extensive and repetitive code. A complete conflict management routine does the following:

  • Detects a conflict.
  • Identifies the nature and location of the conflict.
  • Provides enough information so that the user can intelligently resolve the conflict.

For an example of a conflict management routine, see the data checker class in Samples.vcx, located in the Visual FoxPro ...\Samples\Classes directory. Just add the class to a form and call the CheckConflicts method before any operation that writes buffered data to the table, for example moving the record pointer if you're using row buffering, closing a table, or issuing TABLEUPDATE( ).

See Also

Management of Conflicts | Programming for Shared Access | Management of Updates with Views | GETFLDSTATE( ) | GETNEXTMODIFIED( )