sábado, enero 11, 2014

¿Cómo usar una tabla FoxPro varias veces sin que se cierre?

[2015-04-24 Actualizado con nuevo ejemplo]

Por: Fernando D. Bozzo

El comando USE es muy potente y tiene varios parámetros que están muy bien explicados en la ayuda.

Normalmente en un sistema multiusuario las tablas se abren compartidas (SHARED) para que varios usuarios puedan usar la misma tabla a la vez, y además muchas veces se dá la necesidad de abrir una misma tabla más de una vez en diferentes lugares (forms, clases, programas, etc), y la cláusula AGAIN y ALIAS existen con ese fin:


USE <Tabla> SHARED AGAIN ALIAS Tabla_1


Muchos programadores no suelen anticipar el uso multiusuario de una tabla y simplemente la abren SHARED creyendo que eso es todo... pero luego ven que en un momento dado uno de los sitios que usan la tabla la cierra, y el otro que usaba lo mismo falla porque no la encuentra abierta, ¿qué hacer entonces?

Ayuda mucho tener una nomenclatura para las variables y los ALIAS, de manera que esto se puede resolver muy fácil.



Dos o más formularios usando la misma tabla


Esta es una situación muy habitual, tener varios formularios que usan la misma tabla simultáneamente y hasta puede que con distintos índices en cada sitio, pero compartiendo los mismos datos.

Una forma de poder aprovechar esos datos e independizarse de qué haga el resto del sistema con ella es esta:


* Form Clientes usando la tabla Precios
USE PRECIOS SHARED AGAIN ALIAS cli_PRECIOS

* Form Proveedores usando la tabla Precios
USE PRECIOS SHARED AGAIN ALIAS prv_PRECIOS


¿Y si la tabla se abre en el entorno de datos (DataEnvironment)?
Fácil, se abre el entorno de datos, se elige la tabla PRECIOS y en la ventana de Propiedades se le cambia el ALIAS como se indicó antes.

Otra opción es usar sesiones de datos privadas, de esa forma se puede usar el mismo nombre de tabla sin cambiarle el alias.



Rutinas de consulta (SQL, LOCATE, SEEK, etc)


Cuando se separa un sistema en N capas (por ejemplo: DATOS, INTERFAZ, NEGOCIO), se hacen los métodos de consulta por separado y generalmente en clases específicas para accesos a datos. Es en estos métodos donde suele haber también el mismo problema que con los forms, tablas que se abren en distintos métodos para hacer distintas consultas, y que si están bien encapsulados (aislados) deberían abrir la tabla, hacer la consulta, pasar los datos a una variable o array, cerrar la tabla y devolver los datos.

Esta sería una forma de método de consulta encapsulado y optimizado:

PROCEDURE consultarPrecioPorArticulo
   LPARAMETERS tcCodArticulo, taPrecio

   TRY
      LOCAL lnSelect, loEx as Exception
      lnSelect = SELECT()
      SELECT 0

      USE PRECIOS SHARED AGAIN NOUPDATE ALIAS __Precios

      SELECT Precio FROM __Precios ;
         WHERE CodArtic == tcCodArticulo ;
         INTO ARRAY taPrecio

   CATCH TO loEx
      THROW

   FINALLY
      USE IN (SELECT("__Precios")) && Siempre cierro la tabla
      SELECT (lnSelect) && Restauro el área de trabajo original
   ENDTRY
ENDPROC


En este caso, y por el carácter temporal de la apertura (abre, consulta, cierra) antecedo el ALIAS con uno o dos guiones bajos, que es lo que suelo usar en estos casos. También notar el NOUPDATE en este caso, que hace una apertura ReadOnly (de solo lectura), que es más eficiente y rápido que el de escritura cuando se sabe que no se quieren actualizar datos.



Encapsulación en métodos de Negocio


Un sitio donde es muy útil usar este mecanismo de USE AGAIN es en los métodos de negocio para favorecer la encapsulación.

Seguramente muchos conozcan esta forma de abrir tablas o reutilizar las ya abiertas:

LOCAL llAbiertaAqui
IF NOT USED("MITABLA")
   USE MITABLA IN 0
   llAbiertaAqui = .T.
ENDIF
SELECT MITABLA
SET RODER TO INDICE

*...(el código del método)

IF llAbiertaAqui
   USE
ENDIF



Esta simple rutina adolece de varios problemas, veamos:
  1. Abre la tabla sin indicar el tipo de acceso deseado (EXCLUSIVE, SHARED, NOUPDATE, etc)
  2. ¿Qué ocurre si al intentar reutilizar la tabla que había abierta, la misma estaba como Sólo-Lectura y resulta que vamos a necesitar usar REPLACE? => Error en puerta!
  3. Vean todo el código necesario solamente para usar una tabla!
  4. ¿Y qué ocurre si dentro de "el código del método" llamamos a otro procedimiento que nos cambia el área de trabajo o el puntero de registro? => Otro error!
  5. Cuando reusemos la tabla que había abierta y cambiemos el puntero del registro, por ejemplo con un Go Bottom, Scan, Locate, etc, ¿qué puede ocurrir al retornar al método llamador? => Otra posibilidad más de error!
  6. Estamos estableciendo un índice, con lo que al retornar al módulo llamador, si olvidamos poner el que había.... otro error!

Como se ve, no es buena idea usar esto en los procedimientos, ya que pueden ocurrir varios errores de distinto tipo, pero en todo caso requerirá controlar varias situaciones más que usando simplemente esto:

SELECT 0
USE MITABLA SHARED AGAIN ALIAS _MITABLA ORDER TAG INDICE
 
*...(el código del método)

USE IN (SELECT("_MITABLA"))

...¿a que es mucho menos código? :D

Pero además evitamos otros problemas:

- Al usar otro alias, el puntero de registro original no lo estamos modificando y nos ahorramos el tener que guardarlo y restaurarlo
- Abrimos la tabla con el tipo de acceso que nos interesa (Escritura, Solo-Lectura, etc)
- Abrimos la tabla con el índice que nos interesa, sin tener que restaurar el original
- ...y todo esto sin afectar a la tabla abierta anteriormente, ni al módulo llamador, ni utilizando un handle (manejador) de archivo extra, ya que Fox sabe si la tabla la tiene abierta en otro sitio, y reutiliza el handle.





Resumen


Las cláusulas SHARED, AGAIN y ALIAS permiten usar múltiples veces la misma tabla con diferentes nombres, además eso permite que cada sitio abra la tabla con el índice que requiera, tiene su propio puntero de registro que no será afectado por otros programas o métodos y no consume recursos extra, por lo que es la solución ideal en todos los casos.


Hasta la próxima! :D

1 comentario: