Mostrando las entradas con la etiqueta DBF. Mostrar todas las entradas
Mostrando las entradas con la etiqueta DBF. Mostrar todas las entradas

20 de julio de 2022

Cómo consumir datos de Visual FoxPro en sistemas de 64 bits utilizando un servidor vinculado

Cómo consumir datos de Visual FoxPro en sistemas de 64 bits utilizando un servidor vinculado

Autor: Carlos Alejandro Perez (QEPD) 1965-2021 (Chaco, Argentina)

Publicado en: http://logica10mobile.blogspot.com/2012/05/como-consumir-datos-de-visual-foxpro-en.html

Carlos Alejandro Perez



Introducción

Nos hemos puesto un poco nostálgicos, así que acá volcamos nuestra experiencia reciente en integrar datos de Visual Foxpro (32 bits) en un entorno de IIS de 64 bits, que no podía reducirse a correr 32 bits por condiciones de borde de la instalación.
Como es sabido, los sistemas operativos han ido migrando lentamente hacia los 64 bits. Entre otras ventajas, se tiene mayor direccionamiento en memoria RAM, y un rendimiento más sólido en los entornos de 64 bits, y lo más importante, el foco en los esfuerzos de las grandes compañías, entre otras ventajas. La migración de 16 a 32 bits demoró casi una década, pero de 32 a 64 está ocurriendo muy rápidamente.
En esta coyuntura, la realidad es que todavía existen muchos sistemas desarrollados en torno a Visual Foxpro, que es una aplicación de 32 bits. Éste puede correr en sistemas operativos Windows de 64 bits bajo la emulación WoW64 (Windows on Windows 64), que es esencialmente un conjunto de tres bibliotecas de enlace dinámico. Estas se encargan de colocar una capa muy liviana de adaptación en el sistema de 64 bits, a fin de traducir las llamadas y las estructuras de los sistemas de 32 bits, sin que éstos sufran modificación alguna. Sin embargo, por diseño, los manejadores de dispositivos se manejan de forma distinta. De esta forma, si por ejemplo tenemos una placa de video de tal marca y modelo, y la queremos usar en un sistema de 64 bits, deberemos indefectiblemente conseguir el driver de 64 bits. Como toda máquina virtual, es sumamente difícil comunicarse fuera de ella. En otras palabras, los drivers de 32 bits se podrán ver sólo dentro del ámbito Wow64, es decir: en un sistema operativo de 64 bits, los drivers de 32 serán visibles para los procesos emulados de 32 bits, pero no lo serán desde fuera de ese ámbito.
Ahora, ¿qué tiene que ver esto con Visual FoxPro? Bueno, fundamentalmente, las estrategias de acceso a datos que corren en Windows son basadas en el modelo ODBC (Open Database Connectivity). ODBC, en la implementación para Windows, no es otra cosa que un manejador de impresora modificado para bases de datos, es decir, funciona y se gestiona de manera muy parecida a un device-driver de impresora, y por lo tanto, pueden coexistir drivers de 32 y 64 en un sistema, pero sólo serán visibles dentro de sus ámbitos respectivos de ejecución. El rol del driver ODBC es análogo al manejador de impresora: así como un controlador de una determinada impresora “entiende” el lenguaje y los comandos de la misma (sean ésos PCL, Postcript, etc.), del mismo modo un manejador ODBC “entiende” las particularidades de una determinada base de datos. Y con OLEDB pasa más o menos lo mismo, recordemos que OLE DB es un derivado de ODBC, que introduce el modelo de objetos COM a ODBC, y copia su modelo de doble búfer, etc., con lo cual hay mucha similitud conceptual en cuanto al driver en sí (y no tanta similitud en cómo expone los datos hacia el cliente).

Escenarios posibles entre cliente y servidor

Suponiendo que tenemos un ejecutable de una aplicación cliente que desea acceder a un servidor de datos. Las combinaciones posibles serían estas, siempre hablando de un sistema operativo de 64 bits:
ClienteServidorDriver de 32 bits instaladoDriver de 64 bits instalado
32 bits32 bitsVisible desde el cliente, conectable.Invisible desde el cliente, innecesario para conectarse
32 bits64 bitsVisible desde el cliente, conectable.Invisible desde el cliente, innecesario para conectarse.
64 bits32 bitsInvisible desde el cliente, no se puede conectar.Visible desde el cliente, conectable
64 bits64 bitsInvisible desde el cliente, no se puede conectarVisible desde el cliente, se puede conectar.
Luego, si de VFP y de SQL Server se tratase, podemos asumir como cliente a VFP, y como servidor a SQL Server, y el cuadro de arriba sería válido igualmente. Si nos preguntamos si un cliente de 64 bits podría conectarse a un servidor que corre en 32 bits, la respuesta es: si, en la medida que exista el driver del lado del cliente. Supongamos que existiera un VFP de 64 bits, y se corre una instancia de SQL Server de 32 bits. Como SQL Server es un “servicio de datos” que funciona un un protocolo binario (esto es, basado en sockets con tráfico bajo un modelo propietario) llamado TDS (Tabular Data Stream), tendremos que este servicio es independiente de la versión (32 o 64 bits) que lo produzca: el protocolo es inmutable entre dichas plataformas. Para que lo veamos mejor, un servidor IIS de 32 bits producirá el mismo HTML que uno de 64 bits, ya que el protocolo de comunicación HTTP y el formato del mensaje HTML es conceptualmente siempre el mismo (un documento de texto), independientemente de la plataforma que lo genere. Análogamente, es posible entonces conectarse desde un cliente de 32 a un servidor de 64, siempre y cuando exista el driver de 32 bits para dicho servicio.

Por ejemplo, si queremos gestionar desde un cliente de 64 bits la creación y el mantenimiento de un driver ODBC, sólo veremos los drivers de 64 bits. Hagamos la siguiente prueba: iniciemos el Panel de Control, vayamos a Herramientas administrativas, Configurar orígenes de datos (ODBC). Pidamos crear una nueva conexión de sistema (DSN de sistema), y veremos sólo los drivers de 64 bits que la PC tenga instalados en ese momento:

image
Fig. 1: drivers de 64 bits visibles desde un cliente de 64 bits (Panel de Control)


Ahora bien, sin cambiar de PC, tratemos de hacer lo mismo desde un cliente de 32 bits, por ejemplo, desde el mismo Visual FoxPro, creando una base de datos .DBC y generando una conexión desde allí.

image
Fig. 2: drivers de 32 bits visibles de un cliente de 32 bits (VFP)


Como se aprecia en la figura 2, la cantidad es mayor, y entre ellos , vemos el driver para Visual FoxPro instalado. Tanto en el cliente de 64 bits (Panel de control) como en el cliente de 32 (VFP) podremos gestionar, por ejemplo, la creación de una conexión a SQL Server (independientemente de si el servidor SQL corre en 32 o en 64), porque tenemos ambos drivers de cliente instalados en la PC. Pero no podremos gestionar una conexión a VFP desde un cliente de 64, porque no existe el driver.

El problema con ASP.NET, VS y Windows de 64 bits.

Ahora bien, desde Visual Studio 2010, ¿qué sucede?. Supongamos este escenario:
  • Sistema operativo Windows de 64 bits –> IIS de 64 bits, que contiene:
    • IDE de Visual Studio 2010 convencional, 32 bits, instalado con ajustes por defecto sobre el Windows anterior.
    • Visual FoxPro, 32 bits
    • OLE-DB para Visual FoxPro, 32 bits
El objetivo es construir una página ASP.NET que acceda a los datos de VFP en ese escenario.
El problema es el siguiente:
  • Al programar la aplicación ASP.NET en VS2010, se puede crear sin problemas una conexión VFPOLEDB, ya que el cliente (justamente la IDE de Visual Studio) y el driver OLE-DB son de 32 bits. Al depurar la aplicación ASP.NET, se podrán consumir los datos de VFP con mucha facilidad, por ejemplo, utilizando un control SQLDataSource desde la misma página.
  • Pero al publicar el sitio en IIS, se transfieren los ensamblados a un entorno de ejecución de 64 bits. Las páginas se ejecutan bajo una máquina virtual .NET, que es un proceso de 64 bits. Este CLR de .NET es “incapaz” de registrar el driver de 32 bits OLE-DB para acceder a VFP, y las páginas fallan en IIS dando un error de “dll no registrada”, la de OLE-DB justamente.
Entonces la pregunta es: ¿cómo publicamos una página ASP.NET en un servidor IIS montado en un Windows de 64 bits, que no puede tener compatibilidad de 32 bits por alguna razón, y que la vez consuma datos de Visual FoxPro?
No existe una respuesta fácil. Las posibilidades que exploramos son las siguientes:

Idea centralQué habría que hacerContras
Correr el sitio ASP.NET en IIS habilitado para 32 bits
  • Activar la compatibilidad de 32 bits en IIS de 64 bits, corriendo en una ventana de comandos la siguiente instrucción:


C:\>cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET W3SVC/AppPools/Enable32bitAppOnWin64 true


Si bien se activa la compatibilidad de 32 bits en IIS, al mismo tiempo se inhabilitan las de 64 bits. Luego, deberíamos tener todos los sitios corriendo en IIS en modo de 32 bits, aunque el SO de 64 bits (estamos pagando por algo que no usamos).
Instalar dos IIS en el servidor, uno de 32 y otro de 64La segunda instalación de IIS correría en modo de compatibilidad de 32 bits, sólo afectando a las de 64 en esa instancia, y en la otra podremos correr el sitio en 64 bits. Las páginas que acceden a VFP , al estar publicadas en otro servidor, debería correr en otro port HTTP o bien resolver por nombre (porque tienen una misma IP pública) a nivel del IIS principal.No es posible. No se puede instalar dos IIS en un mismo servidor. Si es posible instalar un IIS + Apache, o bien dos instancias de Apache, pero necesitamos IIS para correr ASP.NET.
Implementar un servidor de automatización COM hecho en VFPCrear un objeto COM (servidor de automatización) con VFP, que reciba las consultas SQL de ASP.NET, y devuelva datasets ADO.NET utilizando el comando CursorToXML()No es posible. No se podrá enlazar el COM en el espacio de 64 de bits del IIS en producción.
Implementar un servicio web con VFP y publicarlo en el mismo IIS que publica las páginas ASP.NET.Utilizar el mismo objeto de automatización COM del punto anterior, pero publicarlo como servicio web en el mismo IIS que sirve el sitio principal.No es posible, el COM de VFP de 32 bits no se podrá enlazar al ISAPI de 64 bits.
Ídem anterior, pero publicar el web service de VFP en un servidor Apache de 32 bits.Instalar en paralelo al IIS un servidor Apache de 32 bits, y publicar el servicio web de VFP de 32 bits por allí. Las páginas principales ASP.NET en IIS de 64 bits consultarán al web service local para acceder a los datos VFPTeóricamente posible, pero complicado. Al tener dos HTTP servers bajo una misma IP, uno de los HTTP servers debe trabajar en otro port, o bien transferir el request HTTP por nombre de dominio en el IIS, derivando al Apache de 32 bits las llamadas al servicio web.
Intentar conseguir un driver de 64 bits.Conseguir un driver OLE-DB de 64 bits para VFP provisto por la empresa Sybase, que sirve para su ETL (extracción, transformación y carga).Licencia de Sybase. ¿Dónde se lo consigue sin tener ningún producto Sybase? ¿funciona standalone o necesita un software de Sybase?

La solución del Servidor vinculado (Linked Server)


Tras dos meses de mucho probar y cavilar, se llegó a la conclusión que la mejor solución sería no tocar la infraestructura de IIS 64 bits preexistente, y utilizar el servicio de “servidor encadenado” que tiene SQL Server. El truco consiste en instalar una instancia vacía de SQL Server Express de 32 bits y activar dentro de ella un servidor vinculado o encadenado (linked server), que oficie de enlace entre los datos de VFP y los clientes de 64 bits.

El servidor vinculado o encadenado es esencialmente una pasarela, es decir, una capa de transformación de mensajes. A continuación mostramos un diagrama de bloques conceptual, donde desde un gran sitio ASP.NET en Windows Server de 64 bits, con SQL Server de 64 bits, se accede a VFP del driver VFPOLEDB de 32 bits:


VFP linked server


Figura 3: esquema de bloques de la solución

Microsoft ha incluido, increíblemente podríamos decir, la capacidad de tener servidores encadenados dentro de la instancia Express. El servidor encadenado puede ser cualquiera que soporte un driver OLE-DB para dicha versión (32 o 64 bits). Así, si debemos conectarnos a VFP, deberemos usar VFPOLEDB, que es un driver de 32 bits, y por lo tanto, descargar e instalar la versión de 32 bits de SQL Server Express, descargar e instalar el driver OLE-DB para Visual FoxPro, y estaríamos listos para lograr el acceso desde las páginas ASP.NET corriendo desde un servidor de 64 bits.

Una vez que el servidor encadenado está definido, se puede acceder desde cualquier cliente que tenga un driver de acceso a SQL, sea éste de 32 o de 64 bits. En la figura, el servidor HTTP instalado es IIS7.x, de 64 bits, que aloja un CLR de 64. Dentro de esa máquina virtual CLR, el proveedor administrado ADO.NET para SQL Server será uno regular, nativo, no existirá ningún problema porque sólo se necesitará la visibilidad del servicio de datos. El proveedor administrado hace abstracción de la plataforma del servidor, porque sólo ve los servicios sin importarle los procesos que lo generan. De este modo, desde .NET y con esta solución, se tendrá acceso indistintamente a SQL Server regular, de producción, que puede ser de 64 bits (alternativa de la figura, donde se asume un SQL server prexistente de 64 bits para el sistema principal), y al mismo tiempo, a SQL Server Express de 32 bits que aloja el servidor encadenado, vacío sin tablas nativas.

Configurando el servidor encadenado para que tome el archivo .DBC de Visual FoxPro, se tendrá acceso a todas las tablas de la base de datos Visual Fox, como si estuviesen residiendo de alguna manera en la instancia SQL Server Express.

Configuración paso a paso

1. Descargar e instalar SQL Server Express de 32 bits en el servidor IIS, o bien en algún lugar de la red que sea visible. Luego, hay dar acceso al proceso de SQL Server a la carpeta que contiene las tablas y el contenedor de base de datos de VFP.

Yendo a la carpeta en cuestión, en el explorador de archivos, hacer clic con botón derecho sobre la que contiene las tablas y el contenedor DBC, y seleccionar Propiedades, luego seleccionar la ficha Seguridad. La cuenta que debemos habilitar para acceso no es ninguna de las consabidas Network Service, ni Local System, ni System, sino la denominada MSSQL$SQLEXPRESS, como se muestra en la figura de abajo. El no hacerlo impedirá que se pueda acceder a dicha base de datos.

image

2. Iniciar el administrador de SQL Server, y configurar un servidor encadenado. Para ello abrir el administrador, y hacer clic con botón derecho sobre la instancia correspondiente. Seleccionar el nodo Server Objects, y dentro de él, la opción Linked Servers

image
Fig. 4

3. Verificar drivers. Expandir dicho nodo, y se verá el listado de drivers disponibles en el ámbito de 32 bits. Debe estar visible la opción VFPOLEDB

image
fig. 5

4. Agregar un nuevo servidor vinculado. Hacer clic con botón derecho sobre el nodo Linked Servers para agregar un nuevo un servidor vinculado a los datos VFP. Aparece el siguiente cuadro de diálogo:

image
Fig. 6

Proveer los siguientes datos:

  • Nombre del servidor encadenado: especificar el nombre o etiqueta para el servidor encadenado. Por ejemplo, MIAPPVFP

  • Proveedor: seleccionar OLE DB para Visual FoxPro

  • Nombre del producto: se puede especificar un identificador del producto, en este caso colocar la misma que el nombre del servidor encadenado.

  • Origen de datos: especificar el archivo .DBC de bases de datos de VFP, con el camino completo al directorio. Por ejemplo, C:\MIAPPVFP\DATOS\datos1.dbc

  • Dejar todos los demás campos en blanco, y hacer clic en Aceptar. Asegurarnos que la pantalla de alta quede así:
image
Fig. 7

5. Probar con una nueva consulta. Conectarse a la instancia de SQL Server Express que aloja el servidor encadenado. Recordemos que al no tener bases de datos de usuario de SQL Server, no existirán más que las predeterminadas (master, etc.). Cuando hagamos una consulta al servidor encadenado MIAPPVFP de este ejemplo, no se trabaja contra ninguna base de datos nativa, sino contra la representación interna de la base de datos VFP. En otras palabras, MIAPPVFP es, al mismo tiempo, servidor y base de datos. Debido a esta particularidad, para hacer una selección SELECT a una tabla de VFP, tendremos dos formas de acceder:
  1. Trabajando directamente con el servidor encadenado, armando consultas en Transact-SQL (en la figura 3, el trazo en azul) desde cualquier cliente de datos administrado de .NET. Esta técnica envía strings de consultas a la base de datos SQL Server Express, y el servidor encadenado, a través de VFPOLEDB, realiza la traducción necesaria al dialecto de VFP. Por este motivo, desde ADO.NET no podremos enviar a ejecutar comandos y funciones propias de VFP, como por ejemplo, SELECT DTOS(fecha) AS fecha… , ya que DTOS() no es una función reconocida por T-SQL. El servidor encadenado recibe la petición en T-SQL , la traduce al SQL de Visual FoxPro, ejecuta la consulta, y al recibir la respuesta, acondiciona el conjunto de resultados como si fuese un resultado nativo de SQL Server para enviárselo al cliente que efectuó la petición de datos.

  2. Utilizando la primitiva OPENQUERY de SQL Server, que permite enviar por paso-a-través un string conteniendo comandos nativos de VFP. En este caso, el servidor encadenado pasa a través de sí mismo la consulta sin modificarla, y sólo se encarga de “adaptar” el resultado de respuesta como una respuesta regular de SQL-Server.
5.1 Prueba con sintaxis T-SQL de SQL Server

Para probar si todo funciona bien, escribamos una consulta sencilla sobre una tabla que sabemos preexistente en la DBC de VFP. Nótese la sintaxis de acceso a la tabla. Se utiliza la notación de cuatro segmentos siempre que se acceda de esta manera a un servidor encadenado. Sin embargo, como aquí se trata en realidad de una carpeta y un contenedor de tablas, no existe el concepto de esquema, etc. por lo que estas etiquetas se omiten, dejando solamente los puntos. De esta forma, si el servidor encadenado se llama MIAPPVFP, y la tabla es clientes.dbf, se referencia como FROM miappvfp…clientes (tres puntos), como se muestra en la siguiente figura:

image
fig. 8

5.2 Prueba de consulta con paso-a-través y OPENQUERY

Cuando sea necesario emitir una consulta que sólo puede resolverse a través de VFP, será necesario utilizar el pasfo-a-través. Para lograr esto, será necesario utilizar la función OPENQUERY, en el siguiente formato de consulta:

SELECT <lista de campos> FROM OPENQUERY(<servidor_vinculado>, <string de consulta nativa VFP>)

Donde el <servidor_vinculado> debe especificarse sin comillas, y la consulta VFP nativa debe estar como una constante de caracteres encerrada entre comillas simples:

SELECT nombre,sexo FROM OPENQUERY(miappvfp,’SELECT * FROM clientes’)

image
fig. 9

Con esto configurado, estaremos en condiciones de consumir los datos desde páginas ASP.NET que se publiquen en IIS de 64 bits, pudiendo aprovechar lo que ya sabemos de ADO.NET, etc.

Conexión desde una aplicación de .NET

Para conectarnos desde Visual Studio, el string de conexión a SQL Server es parecido al que usaríamos con tablas regulares, pero en este caso se omite el catálogo inicial o el nombre de base de datos. Por ejemplo:

Data Source=SERVPRINC\SQLEXPRESS;User ID=MiUsuario;Password=MiPassword

Una vez establecido este origen de datos, en nuestro proyecto podremos generar objetos de datos que accedan a la base contenedora de VFP a través del servidor vinculado. Por ejemplo, si utilizásemos el control sqlDataSource para las páginas ASP.NET, podríamos probar una consulta así

image
fig. 10

Donde el secreto está en configurar el datasource SqlDataSource1 de la siguiente manera:

String de conexión: Por ejemplo,  “Data Source=SERVPRINC\SQLEXPRESS;User ID=MiUsuario;Password=MiPassword” reemplazando las credenciales por las correspondientes a nuestra instalación.

Comando SELECT: Al configurar el SqlDataSource1, el asistente nos preguntará cómo recuperar la base de datos. Nótese que no nos permite acceder a ninguna tabla ya que no hemos especificado una base de datos por defecto, y la única opción será especificar una consulta SQL. La opción de procedimiento almacenado no está disponible.

image  image
Fig. 11 y 12

Este comando SELECT debe especificarse con sintaxis T-SQL de SQL Server, no con el dialecto de Visual FoxPro. Nótese que hemos definido un parámetro de consulta en la consulta:

SELECT nombre,doc_nro FROM miappvfp…clientes WHERE doc_nro = @doc_nro

Ajuste del parámetro de la consulta: Como el control SqlDataSource1 acepta varias formas de cargar el parámetro antes de ejecutar la consulta, seleccionamos que lo tome del control del formulario txtDoc, que es el cuadro de texto donde el usuario especifica el número de documento a buscar, colocando esos datos en el asistente:

 image
Fig. 13

Prueba de la consulta:

En el siguiente paso del asistente probaremos la consulta. En nuestro caso, a los fines de verificación solamente, hemos creado varias entradas en la tabla (doc_nro no es clave primaria en este test) con un mismo documento, para ver si recupera correctamente:

image   image
Fig. 14 y 15

Aceptemos todo y el SqlDataSource1 quedará configurado.

Ajuste del control GridView: Para el gridview de la página sólo bastará hacer clic en su smarttag (el pequeño triángulo que aparece en la esquina superior derecha al seleccionar el control con el ratón), y elegir como origen de datos al recién configurado SqlDataSource1.

image
fig. 16

Al hacerlo, inmediatamente quedará configurada la visual del gridview. Podemos cambiar la visual del gridview utilizando la opción AutoFormat, etc. y cambiarle la cabecera a cada columna utilizando la opción Edit Columns. A efectos ilustrativos, vemos cómo cambiar la cabecera de la primera columna, de nombre (que trae de la tabla de datos VFP) a Nombre de cliente.

image
fig. 17

Prueba de la página:

Pulsamos F5 y aguardamos hasta que aparezca la página

image
fig. 18

Colocamos el numero de documento y pulsamos consultar. A los breves instantes tendremos la respuesta:

image
fig. 19

Donde los datos provienen de Visual Foxpro. También podríamos haber utilizado un dataset, etc.

Publicar la página en el servidor IIS de 64 bits.

La prueba de fuego será publicar la página al servidor IIS, de 64 bits. Una vez hecho esto, podremos verificarla para ver si funciona correctamente. Esta es una imagen de la misma página instalada en el servidor de producción:

image
fig. 20

Con lo cual, hemos podido publicar los datos de VFP en un entorno ASP.NET con IIS de 64 bits, consumiendo datos de VFP.

Algunos cuidados a tener

1. Campos de tipo fecha: Cuando se utilicen fechas como parámetros, la consulta utilizando sintaxis de T-SQL no es optimizada en el motor VFP si la columna de filtrado es de tipo fecha. Por algún motivo, OLEBD para VFP ejecuta una exploración secuencial completa de toda la tabla, dando como resultado un rendimiento de consulta sub-óptimo que puede llegar incluso a una decena de segundos, algo inaceptable para una página web. En este caso, si necesitamos optimizar la consulta por parámetros de fecha en la tabla de VFP, la única solución posible será utilizar OPENQUERY como se explica en el punto siguiente.

2. Funciones nativas de VFP. Al utilizar sintaxis T-SQL, no podremos enviar una consulta con funciones propias de VFP, por ejemplo, INSTR() o DTOS() no podrían incluirse en la consulta. Como vimos en el paso anterior, tampoco podríamos optimizar una consulta que se haga sobre fechas de VFP. Para estos casos, debermos utilizar OPENQUERY indefectiblemente, a fin de pasar sólo un string de consulta en dialecto VFP que será procesado desde el mismo motor de VFP, sin pasar por ningun proceso de traducción en el servidor encadenado.

Por ejemplo, supongamos que necesitamos optimizar una consulta por la columna de fecha llamada fechaVenta en una tabla llamada VENTAS debemos seguir estos pasos:
  • indexar la tabla de Visual Foxpro con la función DTOS, y hacer la consulta desde ASP.NET con dicha consulta. Suponiendo que la columna sea fechaVenta, debemos indexarla con INDEX ON DTOS(fechaVenta) TAG fV en Visual Foxpro.

  • en ASP.NET, configurar los parámetros para que coincidan con el patrón YYYYMMDD de VFP que devuelve la función DTOS(). Por ejemplo, si tenemos una variable dFecha as date en .NET, podremos obtener su string equivalente al colocar dim cFechaParam as string = dFecha.ToString(“yyyyMMdd”).

  • Armar la consulta en el SQLDataSource, SQLAdapter, etc. especificando que el SelectCommand sea el sigiuente: 
SELECT * FROM OPENQUERY( miappvfp, ‘SELECT fechaVenta, factura, cliente FROM ventas WHERE DTOS(fechaVenta) = ‘”+cFechaParam+”’)

3. Palabras clave en consulta. Si bien VFP nos deja emitir consultas con nombres de campo o tablas que coincidan con las palabras clave, como por ejemplo llegar al extremo de colocar “SELECT select FROM select”  y que ejecute correctamente en VFP, siempre que haya una tabla llamada SELECT con una columna con nombre SELECT, este tipo de consultas será rechazada por VFPOLEDB en el servidor encadenado, dando un error en en la instancia de traducción si utilizamos las consultas T-SQL. Incluso utilizando OPENQUERY, la consulta podría realizarse correctamente en VFP, pero al rearmar la tabla de resultados, VFPOLEDB dará un error y no se podrá obtener el dataset, etc. desde .NET. Por tanto, debemos asegurarnos que, tanto con consultas en T-SQL como con  las especificadas dentro de OPENQUERY, no se utilicen objetos de la base de datos que coincidan con palabras clave de T-SQL.

Conclusión


Con la facilidad de servidor vinculado (linked server) que tiene SQL Server Express, podremos hacer que los procesos de 64 bits consuman los datos de VFP a través de VFPOLEDB. El SQL Server Express no tiene costo, y el driver OLEDB para VFP es de descarga gratuita. Con esta solución, si bien se agrega una instancia más al servidor principal o a algún otro de la red, tenemos la ganancia es que no se debe alterar el mecanismo en .NET de acceso a los datos, porque seguirá siendo el driver nativo que siempre estará disponible, en sistemas de 32 o de 64 bits. Así, uno puede tener toda una instalación de un gran sistema ASP.NET en Windows Server de 64 bits, y aún así acceder a los datos de Visual FoxPro sin necesidad de alterar en nada la programación ni la instalación en producción del sitio.

19 de septiembre de 2021

Regenerar todos los índices compuestos de una base de datos

Cuando se dañan los archivos índices de una tabla, ejecutar el comando REINDEX a veces no es suficiente. Hay casos en que hay que generar nuevamente cada uno de los índices con el comando INDEX ON y/o ALTER TABLE.

El siguiente código lo utilizo como una herramienta para regenerar rápidamente todos los índices de todas las tablas de una base de datos, y aparte me genera un archivo PRG con nombre "REG_CDX_????????.PRG" (donde "????????" es el nombre de la base de datos) que lo puedo ejecutar posteriormente

Si la base de datos contiene relaciones persistentes e integridad referencial NO ejecuten el código publicado, ni el archivo .PRG generado ya que éstos eliminan todas las etiquetas de índices de la tabla (DELETE TAG ALL) y esta acción también elimina el archivo .CDX y quita las relaciones persistentes entre las tablas y la integridad referencial existentes en la base de datos. Si éste es su caso, puede utilizar la herramienta GenDBC.PRG que está en la carpeta \Tools\Gendbc\ en la carpeta raíz de instalación de Visual FoxPro, que si respalda y restaura las relaciones y la IR.

Usen este código bajo sus propios riesgos. Hagan una copia de seguridad de la base de datos antes de probarlo.

* -----
* Configurando la variable llRegenera en:
*   .T. = si desea que se regeneren todos los índices al momento de ejecutar el código
*   .F. = solo genera un archivo PRG con el código para generarlos posteriormente 
*-----
LOCAL laDBF[1], laTag[1], llRegenera,  lnI, lnJ, lnTAG
LOCAL lcCmd, lcDBC, lcPRG, lcTabla, lnDBF, loExc

CLOSE TABLES ALL
CLOSE DATABASES ALL
CLEAR ALL
SET MEMOWIDTH TO 128
SET SAFETY OFF

#DEFINE CR_LF CHR(13)+CHR(10)

*-- Selecciono DBC
m.lcDBC = GETFILE([DBC])
*-- Solo genera PRG o genera PRG y regenera índices
m.llRegenera = .F.
*-- Nombre del PRG generado
m.lcPRG = [REG_CDX_] + FORCEEXT(JUSTFNAME(m.lcDBC), [PRG])

IF EMPTY(m.lcDBC) OR NOT FILE(m.lcDBC)
  MESSAGEBOX([Debe seleccionar un archivo DBC], 16)
  RETURN
ENDIF

CLEAR
STRTOFILE([*--- Regenera los índices compuestos de las tablas de la base de datos ] + ;
    JUSTFNAME(m.lcDBC) + CR_LF, m.lcPRG, 0)
STRTOFILE([*--- ] + CHRTRAN(TTOC(DATETIME(), 3), [T], [ ]) + CR_LF + CR_LF, m.lcPRG, 1)

OPEN DATABASE (m.lcDBC) && EXCLUSIVE VALIDATE
SET DATABASE TO (JUSTSTEM(m.lcDBC))

STRTOFILE([OPEN DATABASE ("] + m.lcDBC + [") EXCLUSIVE VALIDATE] + CR_LF, m.lcPRG, 1)
STRTOFILE([SET DATABASE TO ] + JUSTSTEM(m.lcDBC) + CR_LF + CR_LF, m.lcPRG, 1)

m.lnDBF = ADBOBJECTS(laDBF, "TABLE")
ASORT(m.laDBF)

FOR m.lnI = 1 TO m.lnDBF
  *-- Recorro todas las tablas de la DBC
  m.lcTabla = ADDBS(JUSTPATH(DBC())) + m.laDBF(m.lnI)

  TRY
    IF m.llRegenera && Necesito abrir en modo EXCLUSIVE
      USE (m.lcTabla) EXCLUSIVE IN SELECT(m.laDBF(m.lnI))
    ELSE
      USE (m.lcTabla) SHARED IN SELECT(m.laDBF(m.lnI))
    ENDIF

    STRTOFILE([USE ("] + m.lcTabla + [") EXCLUSIVE ] + CR_LF, m.lcPRG, 1)

    m.lnTAG = ATAGINFO(laTag)
    m.lcCmd = ""
    FOR m.lnJ = 1 TO m.lnTAG
      *-- Recorro todas las etiquetas de índices
      IF m.laTag(m.lnJ, 2) = [PRIMARY]
        m.lcCmd = m.lcCmd + ;
          [ALTER TABLE ] + m.laDBF(m.lnI) + [ ADD PRIMARY KEY ] + m.laTag(m.lnJ, 3) + ;
          [ TAG ] + m.laTag(m.lnJ, 1) + ;
          [ COLLATE "] + m.laTag(m.lnJ, 6) + ["]  + CR_LF
      ELSE
        m.lcCmd = m.lcCmd + ;
          [INDEX ON ] + m.laTag(m.lnJ, 3) + [ ] + ;
          [TAG ] + m.laTag(m.lnJ, 1) + [ ] + ;
          IIF(m.laTag(m.lnJ, 2) = "BINARY", m.laTag(m.lnJ, 2) + [ ], m.laTag(m.lnJ, 5) + [ ]) + ;
          IIF(EMPTY(m.laTag(m.lnJ, 4)), [], [FOR ] + m.laTag(m.lnJ, 4) + [ ]) + ;
          IIF(m.laTag(m.lnJ, 2) = "CANDIDATE", m.laTag(m.lnJ, 2) + [ ], []) + ;
          [COLLATE "] + m.laTag(m.lnJ, 6) + ["] + CR_LF
      ENDIF
    ENDFOR

    IF m.llRegenera
      DELETE TAG ALL
      PACK
      EXECSCRIPT(m.lcCmd)
    ENDIF

    STRTOFILE([DELETE TAG ALL] + CR_LF, m.lcPRG, 1)
    STRTOFILE([PACK] + CR_LF + CR_LF, m.lcPRG, 1)
    STRTOFILE(m.lcCmd + CR_LF, m.lcPRG, 1)

  CATCH TO m.loExc
    STRTOFILE([* ERROR:] + CR_LF, m.lcPRG, 1)
    STRTOFILE([* ] + m.loExc.MESSAGE + CR_LF, m.lcPRG, 1)

  FINALLY
    USE IN SELECT(m.laDBF(m.lnI))
    STRTOFILE([USE IN SELECT("] + m.laDBF(m.lnI) + [")] + CR_LF + CR_LF, m.lcPRG, 1)
  ENDTRY

ENDFOR

MODIFY FILE (m.lcPRG)

16 de febrero de 2021

Sentencia JOIN en SQL

 Una manera fácil de entender la clausula JOIN en nuestras sentencias SQL-SELECTs mediante diagramas de Venn.



3 de febrero de 2021

Ahora Stonefield Database Toolkit es de código abierto

Doug Hennig dicidió liberar el código de su herramienta Stonefield Database Toolkit (SDT).

La versión de código abierto de SDT está disponible en https://github.com/DougHennig/StonefieldDatabaseToolkit

La descarga incluye todo el código fuente, la documentación (como un archivo de ayuda CHM, incluyendo la fuente del generador de ayuda HTML de West Wind) y archivos de tutoriales. Todo para poder disfrutar de ésta herramirenta

A continuación una traducción libre al español de la introducción del archivo de ayuda de Stonefield Database Toolkit.


Bienvenido a Stonefield Database Toolkit (SDT)

Stonefield Database Toolkit (SDT) es una poderosa herramienta escrita por Doug Hennig y comercializada durante más de dos décadas por Stonefield Software Inc. Desde el mes de enero, Stonefield Software liberó el código de SDT, poniéndolo a disposición en forma gratuita, para todos los desarrolladores de Visual FoxPro.

SDT proporciona herramientas de administración de bases de datos a los desarrolladores, para ayudar tanto durante el desarrollo de aplicaciones como en tiempo de ejecución.

Hay tres aspectos de Stonefield Database Toolkit:

  • Mejora las herramientas que proporciona VFP para administrar objetos de datos.
  • Proporciona la capacidad de definir propiedades extendidas para objetos de base de datos y establecer u obtener los valores de estas propiedades en tiempo de ejecución.
  • Incluye una biblioteca de clases que puede agregar a sus aplicaciones para proporcionar funciones de administración de datos en tiempo de ejecución, incluida la recreación de índices, la reparación de encabezados de notas y tablas dañados y la creación y actualización de estructuras de tablas en los sitios de los clientes.

Al usar SDT, obtendrá los siguientes beneficios:

  • Reducción del tiempo de desarrollo: ¿Por qué reinventar la rueda? Las rutinas basadas en datos incluidas con SDT le liberan de escribir muchas de las rutinas de mantenimiento de tablas que exigen los usuarios sofisticados de hoy. Las rutinas basadas en datos escritas una vez se pueden incluir en cada aplicación que desarrolle sin modificaciones. Simplemente agréguelos al proyecto, llámelos desde algún lugar (un menú, un formulario o un programa) y olvídese de ellos.
  • Esfuerzo de mantenimiento reducido: el término "basado en datos" significa que el diccionario de datos controla la aplicación. Cambie el diccionario de datos y muchos aspectos de la aplicación se ajustarán automáticamente sin programación.
  • Documentación automática: El Asistente de documentación, que se incluye con Visual FoxPro, documenta los archivos de su aplicación, pero está limitado en la documentación de sus datos. SDT puede producir automáticamente documentación de alta calidad, describiendo completamente sus tablas y vistas, sus campos, índices y propiedades extendidas.

SDT viene con código fuente completo. Puede instalarse en cualquier carpeta de su computadora o red. Nota: si una versión anterior de SDT está instalada en su computadora, es posible que desee instalar esta versión en una carpeta diferente para evitar sobrescribir la versión anterior.

Encontrará dos subcarpetas en la carpeta donde instaló los archivos:

  • SDT: los archivos específicos de SDT.
  • SFCOMMON: los archivos comunes utilizados por todos los productos Stonefield.

La subcarpeta SDT contiene los siguientes archivos:

  • REINDEX.PRG, REPAIR.PRG y UPDATE.PRG: contenedores para los métodos SDT Reindex(), Repair() y Update(). Consulte el capítulo Tutorial para obtener mas información sobre el uso de estos programas.
  • SDT.APP, SDT.PJX y SDT.PJT: la aplicación de desarrollo SDT y los archivos del proyecto que la crean.
  • SDT.CHM: el archivo de ayuda de SDT.
  • Además de estos archivos, encontrará cinco subcarpetas mas en la subcarpeta SDT:

    • DBCX: proporciona documentación para DBCX.
    • HTMLHELP: la fuente para SDT.CHM usando el generador de ayuda HTML de West Wind.
    • SOURCE: código fuente de la aplicación principal (SDT.APP) y las bibliotecas de clases visuales que incluirás en tus aplicaciones.
    • TUTORIAL: archivos para el tutorial SDT.
    • UTILITY: programas de utilidades y archivos de apoyo. Consulte la sección Funciones de utilidades en SDT.CHM para obtener mas información sobre estas utilidades.

    Nota: Todo el código fuente que viene con SDT se compiló en Visual FoxPro 9. Si usa una versión anterior de Visual FoxPro, debe volver a compilar todos los archivos. La forma más sencilla de hacer esto es abrir el proyecto SDT en la carpeta SDT, hacer clic en el botón Generar, y marcar la opción Recompilar todos los archivos y elegir Reconstruir proyecto.


    6 de octubre de 2020

    Tip: Use _TALLY

    _TALLY es una variable de ambiente de VFP, ha estado ahí desde que yo recuerde, su uso puede ser bastante útil cuando se está trabajando con datos. Se utiliza para saber el número de registros que han sido afectados o creados.

    En lo particular, utilizo mucho _TALLY para saber si ha sido creado un cursor con SELECT-SQL:

    SELECT iID, cClave, cNombre, cTelefono ;
      FROM Empleado ;
      WHERE iID = lnIDEmpleado ;
      INTO CURSOR cEmpleado
    
    IF _TALLY > 0 
      ** Se creó el cursor, hubo datos
      ** por lo que podremos procesar cEmpleado
    ELSE
      ** Mensaje al usuario?
    ENDIF

    Está documentado en la ayuda del producto con qué otros comandos se puede utilizar, no estaría de más darle un vistazo e implementarlo en donde sea conveniente.

    Como nota adicional, cabe mencionar que _TALLY no funcionará con cadenas enviadas vía ODBC u OLEDB (hacia un servidor de bases de datos?), ya que le resulta imposible saber (por éste método) cuántos registros han sido afectados.

    Espartaco Palma Martínez

    17 de agosto de 2020

    Convertir un cursor SPT en una vista remota

    Como convertir un cursor SPT (SQL Pass-Thru) en una vista remota para hacer más fáciles las actualizaciones a los datos.

    Una vista remota es un cursor SQL Pass-Thru (SPT) con un "envoltorio de vista" especial que permite que el cursor remoto responda a las funciones TABLEUPDATE(), TABLEREVERT() y REQUERY() de VFP, haciendo más fáciles las actualizaciones a los datos (sin necesidad de escribir tediosas declaraciones SQL INSERT, UPDATE y DELETE).

    Sin embargo, algunos desarrolladores VFP sienten que SPT es superior a las vistas remotas, y quieren hacer el trabajo extra necesario para escribir el código de las actualizaciones. Ellos también pueden preferir reducir su mantenimiento adicional, eliminando vistas remotas de un contenedor de base de datos de VFP.

    Este artículo demuestra que usted puede usar la función CURSORSETPROP() para convertir un cursor SPT en una vista remota, la cual puede ser actualizada facilmente utilizando la función TABLEUPDATE().

    El siguiente PRG demuestra esta técnica, usando la tabla Authors (Autores) de la base de datos Pubs (Publicaciones), contenida en los ejemplos de SQL Server. A fin de ejecutar el PRG con éxito, tendrá que modificar la línea SQLSTRINGCONNECT() para especificar una cadena de conexión que funcione en su computadora.

    El procedimiento local RemoteSPTCursor2RemoteView() en el PRG, es una rutina genérica que convierte cualquier cursor SPT en una "vista remota", con lo cual las actualizaciones son fácilmente llevadas a cabo con una simple llamada TABLEUPDATE().

    La única diferencia entre un cursor SPT convertido en una vista remota en tiempo de ejecución y una vista remota existente (contenida en una base de datos de VFP) es que no puede hacer un REQUERY() a un cursor SPT convertido en una vista remota. Toda la configuración CURSORGETPROP() funciona, el almacenamiento en buffer (y las funciones permitidas) funcionan, y hasta la función REFRESH() funciona.

    Este artículo se aplica a todas las versiones de VFP, pero el siguiente código, requiere VFP 7.0 o superior.

    *
    * Ejemplo de convertir un cursor SPT en una "vista remota"
    *
    * El código interesante está en el procedimiento local
    * RemoteSPTCursor2RemoteView(), que hace todo el
    * trabajo, y que puede modificar para su propio uso
    *
    CLEAR
    LOCAL lnHandle, lnGNM
    *
    * IMPORTANTE!
    * La línea siguiente del código tendrá que ser modificada
    * para especificar una cadena válida SQLSTRINGCONNECT()
    * para establecee una conexión a la base de datos Pubs
    *
    WAIT WINDOW "Intentanto conectar a la base de datos Pubs." + CHR(13) + ;
      "Si este intento falla, debera modificar el programa en " + CHR(13) + ;
      "la línea SQLSTRINGCONNECT() para especificar una " + CHR(13) + ;
      "cadena de conexión que funcione en su computadora." NOWAIT
    *
    lnHandle = SQLSTRINGCONNECT("DRIVER=sql server;SERVER=(local);UID=sa;PWD=;DATABASE=Pubs")
    *
    WAIT CLEAR
    IF lnHandle < 1
      MESSAGEBOX("No puede establecer una conexión a la base de datos Pubs en " + ;
        "SQL Server. Modifique la línea SQLSTRINGCONNECT() para especificar " + ;
        "una cadena de conexión que funcione en su computadora.", 16, "Aviso")
      RETURN
    ENDIF
    IF SQLEXEC(lnHandle,"SELECT * FROM AUTHORS ORDER BY Au_LName") < 0
      MESSAGEBOX("No puede hacer SELECT * FROM AUTHORS", 16, "Aviso")
      SQLDISCONNECT(0)
      RETURN
    ENDIF
    SELECT SQLResult
    *
    * Aquí está donde convertimos el cursor SPT en una vista remota
    *
    IF NOT RemoteSPTCursor2RemoteView("SQLResult", "Authors", "Au_ID", 5)
      MESSAGEBOX("No puede convertir SQLResult en una vista remota.", 16, "Aviso")
      SQLDISCONNECT(0)
      RETURN
    ENDIF
    WAIT WINDOW "Haga cambios a los datos," + CHR(13) + ;
      "(Insert/Update/Delete)" + CHR(13) + ;
      "y cierre la ventana Examinar" NOWAIT NOCLEAR
    BROWSE LAST
    WAIT CLEAR
    lnGNM = GETNEXTMODIFIED(0,"SQLResult")
    IF lnGNM = 0
      MESSAGEBOX("El buffer esta limpio, aparentemente no hizo cambios.", 48, "Aviso")
    ELSE
      *
      * El buffer esta 'sucio'
      *
      GOTO (lnGNM)
      MESSAGEBOX('GetNextModified(0,"SQLResult"): ' + ;
        TRANSFORM(GETNEXTMODIFIED(0,"SQLResult")) + CHR(13) + ;
        'GetFldState(-1,"SQLResult"): ' + TRANSFORM(GETFLDSTATE(-1,"SQLResult")) + CHR(13) + ;
        'Presione "OK" para intentar el TABLEUPDATE(.t.,.t.,"SQLResult")', 48, "Aviso")
      IF TABLEUPDATE(.T.,.T.,"SQLResult")
        *
        * Tuvo éxito!
        *
        MESSAGEBOX("Todas las modificaciones se hicieron exitosamente " + ;
          "con TABLEUPDATE() - La ventana Examinar muestra " + ;
          "un nuevo SELECT * FROM AUTHORS.", 48, "Please Note")
        SQLEXEC(lnHandle,"SELECT * FROM AUTHORS ORDER BY Au_LName")
        WAIT WINDOW "Nuevo " + CHR(13) + "SELECT * FROM AUTHORS" + CHR(13) + ;
          "conteniendo cualquier cambio " + CHR(13) + "que Ud. hizo." NOWAIT NOCLEAR
        BROWSE LAST
        WAIT CLEAR
      ELSE
        *
        * Falló
        *
        LOCAL laError[1]
        AERROR(laError)   &&& laError[1] = 1526
        MESSAGEBOX("El TABLEUPDATE() falló porque " + ;
          TRANSFORM(laError[2]) + "/" + TRANSFORM(laError[3]), 16, "Aviso")
      ENDIF
    ENDIF
    SQLDISCONNECT(0)
    RETURN
    *
    * --
    *
    PROCEDURE RemoteSPTCursor2RemoteView
      *
      * Convierte un cursor SPT en un vista remota actualizable
      *
      *  lParameters
      *
      *   tcCursorAlias (R) Alias del cursor SPT
      *   tcTableName (R) Nombre de la tabla remota de la cual 
      *                   tcCursorAlias fue recuperado
      *   tcPKFieldName (R) Nombre del campo en tcCursorAlias 
      *                     que es la llave (primaria)
      *   tnBuffering (O) Especifica el modo de almacenamiento de buffer 
      *                   para tcCursorAlias, 
      *                   por defecto 3 - Optimista de Tabla
      *   tnWhereType (O) Especifica la propiedad WhereType, 
      *                   por defecto 3 - Clave y Modificado
      *   tlExcludePK (O) Bandera lógica que indica si hay que excluir el
      *                   campo de PK del UpdatableFieldList - pasa este
      *                   parámetro como .T. cuando el campo de PK es
      *                   poblado en virtud de ser una columna de Identidad
      *
      LPARAMETERS tcCursorAlias, tcTableName, tcPKFieldName, ;
        tnBuffering, tnWhereType, tlExcludePK
      *
      * propiedades de actualización - UpdateNameList y
      * UpdatableFieldList, igual que una vista remota
      *
      LOCAL lnSelect, lcUpdatableFieldList, lcUpdateNameList, ;
        lcField, xx, lnCount, llSuccess
      lcUpdatableFieldList = SPACE(0)
      lcUpdateNameList = SPACE(0)
      lcField = SPACE(0)
      lnSelect = SELECT(0)
      lnCount = 0
      SELECT (tcCursorAlias)
      *
      * añadir cada campo al UpdateNameList y 
      * las propiedades UpdatableFieldList
      *
      FOR xx = 1 TO FCOUNT()
        lcField = UPPER(ALLTRIM(FIELD(xx)))
        lnCount = lnCount + 1
        lcUpdatableFieldList = lcUpdatableFieldList + ;
          IIF(lnCount=1,SPACE(0),",") + lcField
        lcUpdateNameList = lcUpdateNameList + ;
          IIF(lnCount=1,SPACE(0),",") + lcField + ;
          SPACE(1) + tcTableName + "." + lcField
      ENDFOR
      IF tlExcludePK
        *
        * Cuando las PKs no deben ser generadas a mano 
        * (como cuando el PK es una columna Identity), 
        * el campo PK tiene que ser quitado del 
        * UpdatableFieldList para prevenir un TableUpdate()
        * e intentar actualizar el campo PK, que 
        * causaría un crash 
        *
        *  ... por cualquier razón, el campo de PK 
        *  debe permanecer en el UpdateNameList...
        *
        lcUpdatableFieldList = "," + ALLTRIM(lcUpdatableFieldList) + ","
        lcUpdatableFieldList = STRTRAN(lcUpdatableFieldList, ;
          "," + UPPER(tcPKFieldName) + "," , ",")
        *
        * asegurar que no dejamos una coma durante 
        * el principio o el final de la cadena
        *
        IF LEFTC(lcUpdatableFieldList,1) = ","
          lcUpdatableFieldList = SUBSTRC(lcUpdatableFieldList,2)
        ENDIF
        IF RIGHTC(lcUpdatableFieldList,1) = ","
          lcUpdatableFieldList = LEFTC(lcUpdatableFieldList,LENC(lcUpdatableFieldList)-1)
        ENDIF
      ENDIF
      llSuccess = .F.
      DO CASE
        CASE NOT CURSORSETPROP("KeyFieldList",tcPKFieldName)
          ASSERT .F. MESSAGE PROGRAM() + " no se puede configurar KeyFieldList"
        CASE NOT CURSORSETPROP("Tables",tcTableName)
          ASSERT .F. MESSAGE PROGRAM() + " no se puede configurar Tables"
        CASE NOT CURSORSETPROP("UpdatableFieldList",lcUpdatableFieldList)
          ASSERT .F. MESSAGE PROGRAM() + " no se puede configurar UpdatableFieldList"
        CASE NOT CURSORSETPROP("UpdateNameList",lcUpdateNameList)
          ASSERT .F. MESSAGE PROGRAM() + " no se puede configurar UpdateNameList"
        CASE NOT CURSORSETPROP("WhereType", ;
            IIF(VARTYPE(tnWhereType)="N",tnWhereType,3))
          ASSERT .F. MESSAGE PROGRAM() + " no se puede configurar WhereType"
        CASE NOT CURSORSETPROP("Buffering", ;
            IIF(VARTYPE(tnBuffering)="N",tnBuffering,3))
          ASSERT .F. MESSAGE PROGRAM() + " no se puede configurar Buffering"
        CASE NOT CURSORSETPROP("SendUpdates",.T.)
          ASSERT .F. MESSAGE PROGRAM() + " no se puede configurar SendUpdates"
        OTHERWISE
          llSuccess = .T.
      ENDCASE
      SELECT (lnSelect)
      RETURN llSuccess
    ENDPROC

    VFP Tips & Tricks - Drew Speedie