You are on page 1of 4

Controlar datos en buffer en Visual FoxPro

Un par de meses atrs escrib sobre buffer; pero deliberadamente dej fuera de la discusin dos funciones vitales mientras
trabajaba con buffer- nombradas TableUpdate() y TableRevert(). Estas son la base mediante la cual usted, el desarrollador,
controla la transferencia de los datos entre el buffer de Visual FoxPro y el origen de datos originales. La funcin
TableUpdate() toma los datos pendientes desde el buffer y los actualiza en la tabla original, mientras TableRevert() refresca
los buffers para releer el dato desde el origen de datos. La realizacin exitosa de otras funciones da como resultado un buffer
'limpio' lo que significa que, para Visual FoxPro, los buffer y el origen de datos son sincronizados.

Controlar el alcance de las actualizaciones


Debido a que Visual FoxPro admite tanto buffer de Filas y de Tablas, ambas funciones de traspaso de datos pueden operar
"slo sobre el registro actual" o sobre "todos los registros modificados". El primer parmetro que se pasa a la funcin
determina el alcance de la operacin y ufff tenemos otra confusin posible aqu. Las funciones operan en modo
significativamente diferente y tienen tipos diferentes de valores devueltos.
En la versin 3.0 de Visual FoxPro; ambas TableUpdate() y TableRevert() aceptaran solamente un parmetro lgico para
determinar el alcance del cambio que estn controlando. Pasar un valor de .T., significa que todos los cambios pendientes
fueron procesados, mientras .F. restringe las operaciones al registro actual solamente, sin importar el modo de buffer
empleado.
TableRevert() devuelve el nmero de filas que fueron revertidos y no pueden realmente "fallar" - sin que fuera un problema
fsico, como perdiendo una conexin de red. En una fila de tabla en buffer, o cuando especficamente el alcance como .F., el
valor devuelto, por tanto siempre 1.
TableUpdate() devuelve un valor lgico que indica si la modificacin especificada es exitosa, independientemente del alcance.
En otras palabras, un valor devuelto de .T., indica que todos los registros en el alcance han sido actualizados con xito. Sin
embargo, el comportamiento cuando se utiliza un parmetro lgico para determinar el alcance y la actualizacin falla por
cualquier razn, es que no se genera un error y la funcin devuelve un valor de .F. dejando el puntero en el registro en que
falla.
Si est actualizando un solo registro, esto es muy sencillo; pero si est actualizando mltiples registros en una tabla, y un
registro no puede ser actualizado, esto significa que cualquier actualizacin posterior no puede ser verificada. Pero, despus
de solucionar el conflicto para el registro que ha fallado, no hay garanta que re-intentar la actualizacin que no va a fallar al
registro ms cercano. Esto puede ser un problema!
El comportamiento de TableUpdate() fue, por tanto, modificado en la versin 5 para aceptar incluso un parmetro numrico
o lgico para el alcance, donde 0 es equivalente para .F. y 1 para .T. El nuevo comportamiento, que puede ser solamente
especificado al pasar "2" como parmetro de alcance, especficamente dirigidos los problemas de actualizacin de mltiples
registros.
Al utilizar buffer de tablas, al llamar TableUpdate() con un parmetro de alcance de "2" Visual FoxPro intenta actualizar todos
los registros que tienen cambios pendientes. Sin embargo, si un registro no puede ser actualizado, en lugar de parar, la
funcin registra el nmero de registro que ha fallado en una matriz (cuyo nombre puede ser especificado como el cuarto
parmetro) y contina tratando de actualizar otros registros cambiados. La funcin devuelve .F. si cualquier registro falla la
actualizacin; pero al menos tratar de actualizar todos los registros disponibles. La matriz de salida contiene una lista de
los nmeros de registros para aquellos registros fallan la actualizacin.

El segundo parmetro para TableUpdate()


Una diferencia mayor entre la sintaxis de TableUpdate() y TableRevert() es que el anterior puede solamente tomar un
parmetro extra lgico, en la segunda posicin en la lista de parmetros. Esto controla la forma en la que se comportan las
actualizaciones cuando encuentran un conflicto.
De forma predeterminada, Visual FoxPro debe rechazar un cambio cuando un conflicto entre el buffer de datos y se detecta el
dato original (vea debajo un debate completo del conflicto y la resolucin). Al especificar un valor lgico .T. como el segundo
parmetro puede forzar una actualizacin a que sea aceptada incluso en situaciones cuando debera fallar. Naturalmente esto
es algo que desear hacer de forma predeterminada; pero existen, como veremos luego, situaciones donde este
comportamiento no es solamente deseado, sino esencial.

Especificar la tabla a ser actualizada o revertida

Ambas funciones TableUpdate() y TableRevert() operan sobre una tabla al mismo tiempo. El comportamiento
predeterminado es que, a menos que especficamente, se determine lo contrario; actuar en la tabla en el rea de trabajo
seleccionada. Si no hay una tabla abierta en el rea de trabajo, hay un error (No se encuentra el alias - Error 13). Ambos, sin
embargo, pueden actuar sobre una tabla abierta disponible en la sesin de datos actual y puede aceptar cualquiera de los
nombres de ALIAS (el tercer parmetro para TableUpdate(), segundo para TableRevert()) o un nmero de rea de trabajo.
No recomendamos el uso del nmero del rea de trabajo en esto, o cualquiera, situacin donde est especificando una tabla
diferente que la seleccionada. Tal y como hemos podido ver esta funcionalidad est incluida, slo por compatibilidad hacia
atrs y no tiene lugar en el entorno VFP. Existen dos razones para evitar el uso del rea de trabajo. Primeramente, hace su
cdigo dependiente de tablas especficas estn abiertas en reas especficas de trabajo - lo que es aun mayor limitacin si
piensa cambiar! En segundo lugar, no tiene control real sobre las tablas abiertas en VFP, los cursores o vistas cuando utiliza
de cualquier forma el Entorno de datos del formulario. Entonces, liberar el nmero del rea de trabajo en lugar del alias, es
una estrategia muy arriesgada e innecesaria.
El nico momento que recomendamos el uso del nmero de rea de trabajo es cuando guardamos el rea de trabajo actual
guardando el valor devuelto de la funcin SELECT(0). Utilizar el nmero del rea de trabajo en este caso asegura que el rea
de trabajo actual est vaca, o que las tablas que contienen se cierren durante cualquier operacin que est haciendo, puede
aun devolverlo sin error.

Conclusin
Existe mucha funcionalidad y flexibilidad oculta dentro de TableUpdate() y TableRevert(). Al utilizar buffer, necesita tener
cuidado de, exactamente cules de varias combinaciones de sus parmetros, puede pasarles para asegurar que est
utilizando la combinacin correcta que necesita. Mientras TableRevert() es bastante simple, TableUpdate() es ms compleja y
por eso, en la Tabla 2 que muestro debajo se brinda un resumen de algunas combinaciones "prcticas" de parmetros para
TableUpdate().
Tabla 2. Opciones de TableUpdate()

Parmetros
Alcanc
e

Fuerz
a

Tabl
a

Salid
a

Accin

0 .F.

.F.

Intenta actualizar la fila actual del alias actual

0 .F.

.T.

Fuerza la actualizacin del registro actual solamente del alias actual

0 .F.

.F./.T.

1 .T.

.F.

Intenta actualizar la fila actual slo del alias especificado

1 .T.

.T.

Fuerza la actualizacin de todos los registros disponibles del alias actual

1 .T.

.F./.T.

Alias

.F.

Alias

.T.

.F./.T.

Alias

Intenta forzar todo los registros disponibles de las alias especificadas

Intenta actualizar la fila actual slo del alias especificado. Se detiene en un fallo
Matriz

Intenta actualizar todos los registros disponibles del alias especificado. Nota los fallos;
pero no se detiene.
Fuerza la actualizacin de todos los registros disponibles del alias actual/especificado

Alias

Matriz

Intenta/Fuerza la actualizacin de todos los registros disponibles del alias especificado.


Nota los fallos; pero no se detiene.

Detectar y solucionar conflictos


La seccin anterior trataba sobre los mecanismos para actualizar una tabla con buffer y en mi artculo anterior, recomend
que el Buffer de Tabla optimista debe ser la opcin normal para la mayora de las aplicaciones. Entonces, el prximo
problema est, en asegurarse de que los cambios no son la causa de un conflicto de actualizacin cuando guarda. Uno de los
problemas inherentes al utilizar bloqueo optimista en un entorno multiusuario es que es posible para ms de un usuario
hacer cambios al mismo registro al mismo tiempo. Puede preguntarse porqu este tipo de cosa pudiera ser posible alguna
vez?- est seguro de que dos usuarios no pudieran actualizar el mismo registro al mismo tiempo?

En la prctica, existen muchas situaciones donde puede ocurrir legtimamente. Considere la situacin en una Orden de
Ventas en el procesamiento del sistema. Cuando se coloca una orden para un elemento, el "stock disponible" actual debe
reajustarse para reflejar la reduccin. Si dos usuarios, que son controlados por dos operadores simultneamente, incluyen el
mismo elemento en sus rdenes, existe un gran posibilidad de que surja un conflicto. Obviamente, esto puede no ocurrir si el
sistema utiliza bloqueo pesimista; pero esto tiene otras consecuencias, generalmente indeseables. En este caso, el segundo
operador, que trata de acceder al elemento en cuestin, recibir un mensaje que el registro est en uso por alguien y no
puede hacer modificaciones - no es mucha ayuda ! An ms, el bloqueo pesimista puede ser utilizado cuando una tabla
Visual FoxPro es utilizada directamente como origen de dato - no puede bloquear de forma pesimista una vista de cualquier
tipo (ni para datos locales o remotos).
Al utilizar buffer, Visual FoxPro hace una copia de todos los datos como e recuperan de la tabla fsica, cuando se pide una
actualizacin, se compara esa copia con el estado actual del dato. Si no hay cambios, la actualizacin es permitida, en otro
caso, la actualizacin se genera un error de conflicto (#1585 para las vistas, #1595 para tablas).

El rol de OldVal() y CurVal()


La base de toda la deteccin del conflicto est en dos funciones nativas, OldVal() y CurVal(), las que acceden a los cursores
intermedios creados por Visual FoxPro cuando utiliza datos en buffer. Como indican sus nombres, OldVal() devuelve el valor
de un campo tal y como era cuando el usuario final ley el dato del origen, mientras CURVAL() devuelve el estado actual del
dato en la tabla origen. Estas dos funciones operan desde el nivel de campo y, aunque ambas pueden aceptar una expresin
que evala una lista de campos, son ms usadas para devolver valores de campos individualmente para evitar el problema
de solucionar diferentes tipos de datos.
Aqu hay un problema al utilizar CurVal() para verificar el estado de la vista. Visual FoxPro realmente mantiene un nivel
adicional de buffer para una vista, la cual, a menos que sea refrescada inmediatamente antes de verificar el valor del campo,
puede causar CurVal() que devuelve la respuesta incorrecta.

Entonces, Cmo puedo realmente detectar el conflicto?


Antes de entrar en la discusin de cmo detectar un conflicto, permtanme ser claro sobre la definicin de Visual FoxPro
sobre lo que es un conflicto. Como he comentado antes, en la discusin sobre buffering, Visual FoxPro realmente hace dos
copias de un registro, cada vez que el usuario accede al dato. Una copia est disponible como cursor modificable y es donde
un usuario hace los cambios. La otra copia guarda el estado original. Antes de permitir la actualizacin, Visual FoxPro
compara este cursor original con el dato guardado actualmente en el disco. Ocurre un conflicto cuando estas dos versiones
del dato no coincide exactamente, y existen dos formas en las que puede ocurrir. La primera, y ms obvia, es debido a que el
usuario actual hace cambios a un registro y trata de guardar esos cambios despus de que otro ha cambiado y guardado el
mismo registro.
La segunda es menos obvia y ocurre cuando un usuario hace un cambio; pero entonces los cambios van a sus valores
originales. El resultado es que no cambia cuando en realidad hace; pero cuando tratan de "guardar" el registro Visual FoxPro
va a ver aun esto como un conflicto debido a que los valores OldVal() y CurVal(), en realidad son diferentes. Para evitar este
tipo de error puede simplemente confiar en GetFldState(); pero debe comparar expresamente los valores en el buffer actual
con estos en OldVal().
Entonces, habiendo evitado la posibilidad de conflictos cuando el usuario actual no ha hecho ningn cambio; pero
sencillamente no trata de confirmar el registro, existen bsicamente dos estrategias que puede adoptar para detectar
conflictos. La primera es el proceder "Trate y vea". Esto significa simplemente que no trata y detecta conflictos potenciales;
pero slo atrapa el resultado de una llamada para TableUpdate() y cuando falla, determina porqu.
El segundo proceder es "Cinturones y tirantes" en el que cada campo cambiado se verifica individualmente y cualquier
conflicto se soluciona antes de intentar actualizar la tabla original. Mientras esto parece ms defensivo, y por tanto "mejor"
en realidad oculta un problema. En el tiempo que esto ocurre (aunque sea muy pequeo) para verificar todos los cambios
contra sus valores actuales, otro usuario puede cambiar exitosamente el mismo registro que est verificando. Entonces, a
menos que tambin haya bloqueado explcitamente el registro antes de comenzar a verificar los valores, la actualizacin real
pudiera fallar. Debido a que queremos realmente evitar explcitamente la colocacin de bloqueos, necesita incorporar
exactamente la misma verificacin del resultado de TableUpdate(), y brinda el mismo control del fallo, lo que es mucho ms
simple precisamente la estrategia "Intenta y veremos".
Por tanto, a menos que tenga una razn para sobreescribir cambios pre-validados, recomendamos fuertemente que permita
que Visual FoxPro detecte los conflictos y justamente atrpelo para aquellos errores que han surgido.

De acuerdo, entonces, habiendo detectado un conflicto de actualizacin, qu


puedo hacer sobre esto?

He aqu cuatro estrategias bsicas para actualizar conflictos. Puede escoger una, o combine ms de una en una aplicacin en
dependencia de la situacin real:
[1] El usuario actual siempre gana - Esta estrategia es apropiada solamente en aquellas situaciones en las que se asume
que son correctos los datos del usuario que est actualmente intentando guardar. Tpicamente esto debera ser
implementado en la base del ID del usuario, el que es en realidad est haciendo el guardado y debera implementar una
regla de negocio que cierta informacin de una gente es ms til que otra.
Un ejemplo podra ser tener una aplicacin donde un operador hable al usuario podra tener derechos de sobreescritura para
contactar informacin para el cliente (en la base que la persona realmente habla al cliente es ms probablemente capaz de
obtener los detalles correctos). El conflicto puede surgir en este caso cuando un administrador est actualizando un detalle
de cliente desde un dato en archivo o ltima orden, mientras un operador tiene detalles nuevos, directamente desde el
cliente. La implementacin de esta estrategia en Visual FoxPro es muy sencilla. Simplemente establezca el parmetro
"FORCE", (el segundo) en la funcin TableUpdate() a ".T." y reintente la actualizacin.
[2] El usuario actual siempre pierde - Esto es exactamente lo contrario a la anterior. Al usuario actual se le permite
solamente guardar los cambios siempre que no hay otro usuario que haya hecho cambios. Por el contrario, ser
implementado normalmente en base al ID del usuario y podra reflejar la probabilidad que este usuario en particular es
propenso a trabajar con informacin "histrica" en lugar de informacin "actual". La implementacin en Visual FoxPro es
tambin muy sencilla. Los cambios del usuario actual se revierten, se recarga la informacin original y el usuario tiene que
hacer cualquier cambio que necesite, una vez ms. Esta es, probablemente la estrategia que es adoptada ms
frecuentemente - pero usualmente en base global.
[3] El usuario actual gana a veces - Esta estrategia es la ms compleja de las cuatro a implementar; pero es en la
actualidad, bastante frecuente. El principio bsico es que cuando ocurre un conflicto de actualizacin, usted determina si
alguno de los campos, que el usuario actual ha cambiado, van a afectar los cambios hechos por otro usuario. Si no, el
registro del usuario actual es actualizado automticamente (utilizando el valor de CURVAL()), entonces esto provoca que es
negado el conflicto y la actualizacin se reintenta. Sin embargo, debido a no puede cambiar los valores devueltos por
OldVal(), necesita forzar la segunda actualizacin.
Incidentalmente, esta estrategia tambin est dirigida al problema de cmo controlar el conflicto de actualizacin "falso
positivo". Esto ocurre cuando existen discrepancias entre los valores del disco y aquellos en el buffer de usuario, pero los
actuales cambios del usuario, no crean en realidad un conflicto con cualquier otro cambio que se haya hecho. Claramente,
esta situacin no es en realidad un conflicto; pero necesita ser controlada.
Aunque no es trivial, la implementacin en Visual FoxPro es relativamente fcil. Primero, la funcin CURVAL() es utilizada
para determinar cmo actualizar el buffer de usuario actual de tal forma que no va sobreescribir los cambios hechos por otro
usuario. Entonces, la actualizacin se aplica utilizando el segundo parmetro FORCE en el TableUpdate() para decirle a Visual
FoxPro que ignore el conflicto que van a surgir porque OldVal() y CurVal() no corresponden.
[4] El usuario actual decide - Este es el caso de "Coge todo". El conflicto no falla bajo ninguna regla de negocio
reconocida, as que la nica solucin es preguntarle al usuario cuya accin de guardar desencadena el conflicto que es lo que
desea hacer. La idea bsica es que usted muestre al usuario que ha desencadenado el conflicto con una lista de valores aquellos que acaba de entrar) y el valor que hay ahora en la tabla ( es decir con el cual alguien ha cambiado el valor
original). El usuario puede entonces decidir si hay que forzar o revertir sus propios cambios. Las herramientas bsicas para la
implementacin de esta estrategia han sido discutidos en secciones anteriores. Todo lo requerido es determinar qu cambios
traen conflicto y los presentan al usuario como va para que el usuario pueda decidir en un campo a campo qu hacer, en
base al conflicto. En la prctica, esta estrategia en general se combina con la estrategia [3] mostrada antes, as al usuario
slo se le presenta una lista de campos donde hay un conflicto en los campos que han modificado por ellos mismos.
En el prximo artculo de esta serie, ver el diseo e implementacin de una clase que controle un conflicto que puede ser
arrastrada a un formulario.

You might also like