Managing Data Conflicts in the Client/Server Sample Application

If the data in the source table is changed while another user is editing it, there is a possible conflict. You have several ways of resolving this conflict in the Update Options area of the Client/Server Sample form.

If you select Force in the Update Options area, any changes to the data that were made after you started editing are written over when you update the data with your changes.

If you choose Resolve Conflicts in the Update Options area, the Client/Server sample application finds conflicts when they occur and makes it possible for you to see the conflicting data and decide to override the existing changes or discard your own changes. If you do not choose Resolve Conflicts, your data will not be updated if a conflict is detected:

IF THIS.parent.chkConflicts.Value
   THISFORM.ResolveConflicts
ELSE
   WAIT WINDOW 'Update failed' NOWAIT TIMEOUT 5
ENDIF

If you choose Business Rules in the Update Options area, the application updates the data according to your business rules. For more information about the Business Rules option, see Implementing Business Rules in the Client/Server Sample Application.

To generate a data conflict

  1. Run two separate instances of Visual FoxPro to simulate multiple users accessing the database.
  2. Run the Client/Server sample application in each instance.
  3. Enable buffering in both sessions.
  4. Make sure the Force check box is not selected in either instance of Visual FoxPro.
  5. Make changes in each session to the same fields in the same row of the table.
  6. Update the table in one of the sessions.
  7. Update the table in the other session.

When you choose to update the table the second time, the following line of code in the Click event of cmdUpdate attempts to update the table.

llUpdate = TABLEUPDATE(lnUpdateType, llForce)

If llForce is set to True (.T.), changes made in the other session are overridden. If llForce is set to False (.F.) a data conflict is detected and the TABLEUPDATE( ) function returns False. If llUdate is set to False, the ResolveConflicts method is called:

IF llUpdate
   WAIT WINDOW 'Update succeded' NOWAIT TIMEOUT 5
ELSE
   frmConflicts = CREATEOBJECT('Conflicts')
   frmConflicts.Show
ENDIF

Code in the Activate event of the Conflicts form creates a cursor with three blank records to display the current values, the old values, and the changed values of the row

=AFIELDS('aEmployee')
SELECT 0
CREATE CURSOR CS_CONFLICTS FROM ARRAY aEmployee

APPEND BLANK
APPEND BLANK
APPEND BLANK

Code associated with the Next method of the Conflicts form populates the cursor with Populate, which is the conflict grid with the old, new, and changed values. For example, the following FOR loop fills a record with the old values in the table:

FOR m.i = 1 TO ALEN(aEmployee, 1)
   REPLACE (aEmployee[m.i,1]) WITH ;
      OLDVAL(aEmployee[m.i,1], lcEmployee)
ENDFOR

When users can see exactly what the data conflicts are, they are in a better position to decide whether to override the new values, revert the changes they made, or ignore the conflict.

Code in the Click event of cmdUpdate on the Conflicts form forces the update:

llUpdate = TABLEUPDATE(.F., .T.)

Code in the Click event of cmdRevert throws away the changes the user made:

lnRows = TABLEREVERT(.F.)

If the user chooses Ignore in the Conflicts form, processing continues on the other conflicts in the table.

See Also

Solutions Samples | Client/Server Sample | Client/Server Sample Application Classes | Client/Server Sample Application Database | Implementing Business Rules in the Client/Server Sample Application | Updating Data in the Client/Server Sample Application