Olá!
Caro Josmar:
parmita-me colocar alguns conceitos:
- quando um programa aplicativo está em execução em um ambiente de rede, além das preocupações normais, surge outra, conhecida como concorrência de acesso a dados em ambiente multiusuário. Isso acontece quando mais de um usuário da aplicação tenta acessar o mesmo registro da mesma tabela ao mesmo tempo. Isso é uma discussão bem longa, que já mereceu vários tratados...
- No bom e velho Clipper, temos que tratar isso nós mesmos, utilizando funções e comandos disponíveis na linguagem.
- basicamente, é preciso saber que se adicionamos um novo registro a uma tabela com sucesso, este registro fica disponível e já bloqueado contra o acesso de outros usuários.
Código: Selecionar todos
CLIENTES->( DbAppend() )
If .Not. NetErr() // conseguiu inserir e deixou o registro travado...
CLIENTES->cod := nCod
CLIENTES->razao := cRazao
// demais campos
CLIENTES->( DbCommit(), DbUnLock() )
Else
Alert( "Falha ao incluir novo cliente." )
Endif
- quando precisamos atualizar um registro em ambiente de rede, primeiro precisamos garantir que o acesso ao mesmo está assegurado apenas para nossa aplicação. Feito isso, podemos atualizá-lo:
Código: Selecionar todos
If CLIENTES->( Rlock() ) // conseguiu travar o registro...
CLIENTES->razao := cRazao
// demais campos
CLIENTES->( DbCommit(), DbUnLock() )
Else
Alert( "Falha ao alterar cliente." )
Endif
Na instalação original do Clipper 5, tem um programa que ilustra essas situações. Pode ser encontrado em :
C:\Clipper5\Source\Sample\LOCKS.PRG:
Código: Selecionar todos
/***
*
* Locks.prg
*
* Sample networking functions to supplant the use of USE,
* FLOCK(), RLOCK() and APPEND BLANK by adding additional
* functionality
*
* Copyright (c) 1993, Computer Associates International Inc.
* All rights reserved.
*
* NOTE: Compile with /a /m /n /w options
*
*/
#include "Common.ch"
#define NET_WAIT 0.5 // Seconds to wait between between retries
#define NET_SECS 2 // Number of seconds to continue retry
/***
*
* AddRec( [<nWaitSeconds>] ) --> lSuccess
*
* Attempt to APPEND BLANK with optional retry
*
* Parameter:
* nWaitSeconds - Optional time in seconds to retry operation, defaults
* to NET_SECS
*
* Returns:
* True (.T.) if successful, false (.F.) if not
*
*/
FUNCTION AddRec( nWaitSeconds )
LOCAL lForever // Retry forever?
DEFAULT nWaitSeconds TO NET_SECS
APPEND BLANK
IF !NETERR()
RETURN ( .T. ) // NOTE
ENDIF
lForever := ( nWaitSeconds == 0 )
// Keep trying as long as our time's not up
DO WHILE ( lForever .OR. ( nWaitSeconds > 0 ) )
APPEND BLANK
IF !NETERR()
RETURN ( .T. ) // NOTE
ENDIF
INKEY( NET_WAIT ) // Wait NET_WAIT seconds (defined above)
nWaitSeconds -= NET_WAIT
ENDDO
RETURN ( .F. ) // Not locked
/***
*
* FilLock( [<nWaitSeconds>] ) --> lSuccess
*
* Attempt to FLOCK() with optional retry
*
* Parameter:
* nWaitSeconds - Optional time in seconds to retry operation, defaults
* to NET_SECS
*
* Returns:
* True if successful, false if not
*
*/
FUNCTION FilLock( nSeconds )
LOCAL lForever // Retry forever?
DEFAULT nSeconds TO NET_SECS
IF FLOCK()
RETURN ( .T. ) // NOTE
ENDIF
lForever := ( nSeconds == 0 )
// Keep trying until our time's up
DO WHILE ( lForever .OR. ( nSeconds > 0 ) )
INKEY( NET_WAIT ) // Wait NET_WAIT seconds
nSeconds -= NET_WAIT
IF FLOCK()
RETURN ( .T. ) // NOTE
ENDIF
ENDDO
RETURN ( .F. ) // Not locked
/***
*
* NetUse( <cDatabase>, <lOpenMode>, [<nWaitSeconds>] ) --> lSuccess
*
* Attempt to USE a database file with optional retry
*
* Parameters:
* cDatabase - Database file to open
* lOpenMode - Sharing mode: True indicates EXCLUSIVE, false
* indicates SHARED
* nWaitSeconds - Optional time in seconds to retry operation, defaults
* to NET_SECS
*
* Returns:
* True if successfull, false if not
*
*/
FUNCTION NetUse( cDatabase, lOpenMode, nSeconds )
LOCAL lForever // Retry forever?
DEFAULT nSeconds TO NET_SECS
lForever := ( nSeconds == 0 )
// Keep trying as long as our time's not up
DO WHILE ( lForever .OR. ( nSeconds > 0 ) )
// lOpenMode determines the mode files are opened in
IF lOpenMode
USE ( cDatabase ) EXCLUSIVE
ELSE
USE ( cDatabase ) SHARED
ENDIF
IF !NETERR()
RETURN ( .T. ) // NOTE
ENDIF
INKEY( NET_WAIT ) // Wait
nSeconds -= NET_WAIT
ENDDO
RETURN ( .F. ) // USE fails
/***
*
* RecLock( [<nWaitSeconds>] ) --> lSuccess
*
* Attempt to RLOCK() with optional retry
*
* Parameter:
* nWaitSeconds - Optional time in seconds to retry operation, defaults
* to NET_SECS
*
* Returns:
* True if successful, false if not
*
*/
FUNCTION RecLock( nSeconds )
LOCAL lForever // Retry forever?
DEFAULT nSeconds TO NET_SECS
IF RLOCK()
RETURN ( .T. ) // NOTE
ENDIF
lForever := ( nSeconds == 0 )
DO WHILE ( lForever .OR. ( nSeconds > 0 ) )
IF RLOCK()
RETURN ( .T. ) // NOTE
ENDIF
INKEY( NET_WAIT ) // Wait 1/2 second
nSeconds -= NET_WAIT
ENDDO
RETURN ( .F. ) // Not locked
Você pode estudá-lo e fazer adaptações para o teu próprio uso.
Boa sorte!