Trabajo en capas - CursorAdapter-PostgreSql

1,585 views
Skip to first unread message

Jorge Gonzalez

unread,
Jun 8, 2015, 6:16:01 PM6/8/15
to publice...@googlegroups.com
Buen día foro.
Voy a comenzar un nuevo proyecto administrando los datos en PostgreSQL y con todo esto de la técnca de encapsulación, me encuentro en la disyuntiva de si usar Cursoradapter o los los comandos SQL
Según hemos visto, una de las capas a usar es la del manejo de datos, y con esto, ya no es necesario el uso de las tablas en buffer, ya que estamos usando objetos de datos y solo abrimos las tablas fisicas al momento de actualizar datos, es decir, no trabajamos con ellas directamente.
El caso de los Cursoradapter es que al momento de usar un =Tableupdate, esta actualiza la tabla de quien fue referenciada, pero si seguimos usando los objetos de datos, veo que con solo usar las actualizaciones, consulta o insert con comandos SQL practicamente es lo mismo.
Que me dicen?

Daniel Sánchez

unread,
Jun 9, 2015, 12:35:47 PM6/9/15
to Comunidad de Visual Foxpro en Español
Los cursoradpaters son una gran muestra de encapsulación de acceso a datos, y que no te preocupas si tus datos están almacenados en un tabla dbf o en un motor externo, en tu caso Postgres.
Yo los recomiendo altamente ya que te olvidas del temas de crear manualmente los update, los insert, lo que si debes recordar que por cada tabla que deseas abrir debe estar con un cursoradapter, y si además de eso creas una clase para el manejo de los registros y acceso a los datos como si fueran objetos mucho mejor, esto te evitará muchos errores comunes como estar en un área o select que no corresponde a los datos a actualizar o eliminar, y código redundante, ya que también estaría encapsulado en la clase.
Yo los utilizo con muy buenos resultados, suerte con tu nuevo proyecto.

Saludos
--
Daniel Sánchez Escobar
Investigación y Desarrollo
Reset Software & Sistemas
Móvil +051-949398047 RPM #948615385
Trujillo - Perú

P  Sugerimos no imprimir este e-mail a menos que sea absolutamente necesario. Protejamos el medio ambiente.

Antonio Meza

unread,
Jun 9, 2015, 1:17:21 PM6/9/15
to publice...@googlegroups.com, sop...@avdelsistemas.com
Hola!!

No eh usado CursorAdapter, pero debe ser algo parecido a Vistas Remotas o Vistas Locales pero mucho mas potente, la ventaja es como menciona Daniel ya no tienes que preocuparte por enviar los comandos SQL al servidor para insertar, actualizar o eliminar, pero sigues usando cursores locales.

Es el mismo principio que uso en FoxyDb. aprovechar la ventaja que te dan los cursores de VFP que son devueltos por SqlExec() desde cualquier servidor de base de datos.

La diferencia de CursorAdaptar con FoxyDb que es FoxyDb permite especificar los parámetros de las transacciones en el servidor y puedes aprovechar mas características del propio servidor a diferencia de CursorAdapter que no lo puedes hacer. la ventaja de CursorAdapter es que puedes cambiarte a cualquier servidor de base de datos mas sencillo, pero estas limitado a ciertas características, por ejemplo no te puedes desconectar del servidor usando cursoradapter, y en foxydb si, y luego mandar a guardar los cambios.

En cuanto a la capa de datos en mi caso sigo aprovechando los cursores en ves de crear objetos de datos, al final el resultado sera el mismo, es decir los cambios que hayas hecho en el cursor o en el objeto de datos deberás retornarlo al servidor, pero la diferencia es que hay muchas funciones que te permiten saber todo lo que hayas hecho sobre un cursor, a diferencia de un objeto de datos que debes controlarlo de forma manual.

Un ejemplo, si insertaste un registro o si actualizaste un registro y la tabla tiene 30 campos y solo modificaste 5, con un cursor puedes generar el Update con los 5 campos modificados aprovechando funciones de VFP, en cambio con un objeto de datos debes manualmente aplicar técnicas para saber cual cambio o de plano enviar los 30 campos.

Si manejas bien PostgreSql puedes colaborar para agregarlo a FoxyDb.


saludos
Antonio Meza

Víctor Hugo Espínola Domínguez

unread,
Jun 9, 2015, 3:31:47 PM6/9/15
to publice...@googlegroups.com
Hola Antonio


>No eh usado CursorAdapter, pero debe ser algo parecido a Vistas Remotas o Vistas Locales pero mucho mas potente,
Es cierto, es como comparar un kayak con un transatlántico.

> pero sigues usando cursores locales.
No necesariamente, por ejemplo pudes insertar un objeto en una tabla mediante cursor adapter:

     Insert Into (m.loCurAdapter.Alias)    ;
         From Name m.toRegistro
     llOK = Tableupdate(.T.)
 
>La diferencia de CursorAdaptar con FoxyDb que es FoxyDb permite especificar los parámetros de las transacciones en el servidor y >puedes aprovechar mas características del propio servidor a diferencia de CursorAdapter que no lo puedes hacer.
La clase cursor adapter tiene una propiedad para activar/desactivar la transacción, en cuanto al tipo de transacción y el nivel de aislamiento solo se deben enviar los comando apropiados antes y después de actualizar la tabla, como lo recomendable es trabajar con una clase propia basada en cursor adapter estos parámetros se pueden incluir en dicha clase.

> la ventaja de CursorAdapter es que puedes cambiarte a cualquier servidor de base de datos mas sencillo, pero estas limitado a ciertas >características, por ejemplo no te puedes desconectar del servidor usando cursoradapter, y en foxydb si, y luego mandar a guardar los >cambios.
Cursor adapter es mucho más flexible porque no es necesario usarlo para traer los datos desde el servidor, los traes con SPT lo manipulas y luego traes un cursor vacío con cursor adapter y con el método ATTACH adhieres el primer cursor al cursor adapter.

>En cuanto a la capa de datos en mi caso sigo aprovechando los cursores en ves de crear objetos de datos, al final el resultado sera el >mismo, es decir los cambios que hayas hecho en el cursor o en el objeto de datos deberás retornarlo al servidor, pero la diferencia es >que hay muchas funciones que te permiten saber todo lo que hayas hecho sobre un cursor, a diferencia de un objeto de datos que >debes controlarlo de forma manual.
El uso de objetos, arrays, cursores, xml, etc. es un tema que depende de como se implementarán las capas, y si los datos serán usados en otras aplicaciones. Si las capas estarán en la misma pc y los datos no serán usados por aplicaciones NO VFP entonces como devuelvas desde la capa de datos es una mera cuestión de gusto.

>Un ejemplo, si insertaste un registro o si actualizaste un registro y la tabla tiene 30 campos y solo modificaste 5, con un cursor puedes >generar el Update con los 5 campos modificados aprovechando funciones de VFP, en cambio con un objeto de datos debes >manualmente aplicar técnicas para saber cual cambio o de plano enviar los 30 campos.
La clase cursor adapter tiene la propiedad WHERETYPE que permite hacer eso, además preocuparse por millonésimas de segundo que se pueden ahorrar grabando 5 en vez de 40 ;-)

Saludos,
Víctor.
Lambaré - Paraguay.

Hugo C.

unread,
Jun 9, 2015, 3:59:14 PM6/9/15
to publice...@googlegroups.com, vich...@gmail.com
Yo que pensaba que conocía el CursorAdapter, buena información Victor.

Saludos.

Hugo C.

unread,
Jun 9, 2015, 5:15:05 PM6/9/15
to publice...@googlegroups.com, vich...@gmail.com
..Si te interesa las puedo compartir con gusto....
Te lo agradecería Victor,  para probarlas, tenia planeado hacer una propia ya que siempre he usado la clase base de Fox y siempre falta esto o aquello que sea mas fácil.

Saludos.

El martes, 9 de junio de 2015, 14:41:41 (UTC-6), Víctor Hugo escribió:
Hola tocayo

Solo conozco lo básico, la clase cursor adapter tiene 76 propiedades y yo uso habitualmente menos de 10, de los 13 métodos y 23 eventos solo uso 1 (uno!) de cada uno; los orígenes de datos pueden ser nativos, ODBC, ADO y XML solamente manejo nativos y ODBC, de ADO y XML NPI :-(

Las clases que uso son modificaciones de trabajos e ideas de otros, una de ellas original de Rafael Copquin y la otra de Cetin Basoz. Si te interesa las puedo compartir con gusto.

Para mi gusto el cursor adapter es el mejor invento después del dulce de leche ;-)

Saludos,
Víctor.
Lambaré - Paraguay.

Víctor Hugo Espínola Domínguez

unread,
Jun 9, 2015, 5:29:13 PM6/9/15
to publice...@googlegroups.com
Ahí van ...

vhecageneric.prg
cageneric.prg
micursoradapter.prg

Hugo C.

unread,
Jun 9, 2015, 5:35:19 PM6/9/15
to publice...@googlegroups.com, vich...@gmail.com
Gracias Victor, tendrás un ejemplo de como usarla de una manera desconectada ?.

Saludos.


El martes, 9 de junio de 2015, 15:29:13 (UTC-6), Víctor Hugo escribió:
Ahí van ...

Antonio Meza

unread,
Jun 9, 2015, 5:47:06 PM6/9/15
to publice...@googlegroups.com, vich...@gmail.com
Gracias por compartir!! me gusta siempre tomar los diferentes puntos de vistas!!

Pero después de ver esos código me sigo quedando con Foxydb. jeje

saludos
Antonio Meza

Antonio Meza

unread,
Jun 9, 2015, 6:13:34 PM6/9/15
to publice...@googlegroups.com, vich...@gmail.com
Cuando empece a investigar cual era mejor si Vistas Remotas, SPT o CursorAdapter, me encontré un comentario de Pablo Roca, que preguntaba cual recomendaban, y luego aparecieron varios Maestros de VFP dando sus puntos de vista, si leen el siguiente post verán de quienes hablo, para que lo lean no les digo jajaja


La verdad que ese post me inclino a usar SPT, primero maneje cursores actualizables con DBVFP pero dependías de conexión permanente al servidor, y con FoxyDb uso cursores locales que no dependen de una conexión permanente al servidor y a demás tienes la opción de manejar directamente SQL en el servidor, como decimos al gusto del cliente.

CursorAdapter es muy potente pero como bien dicen en el post la curva de aprendizaje no es sencilla y mas cuando requieres mas control, en cambio SPT es muy fácil, solo que tienes que ajustarlo a cada servidor de base de datos.

saludos
Antonio Meza

Víctor Hugo Espínola Domínguez

unread,
Jun 9, 2015, 6:53:00 PM6/9/15
to publice...@googlegroups.com
Hola tocayo

No tengo ejemplo de eso debido a que recurro al cursor adapter solo cuando debo actualizar un conjunto de registros con altas, bajas y modificaciones, pero es muy sencillo obtener lo que quieres: Una vez creado el objeto cursoradapter, con el método CursorFill traes los datos al cursor especificado por la propiedad Alias y con el método CursorDetach lo desligas del objeto cursoradapter y de esa forma el cursor no se cierra cuando el objeto sea eliminado.

Cursor adapter es sumamente útil para actualizar (grabar, borrar, modificar) tablas tipo detalle, hay un ejemplo de esto en http://www.mediafire.com/download/oglq0a8od3ac9gg/Menus.rar

Saludos,
Víctor.
Lambaré - Paraguay.

Hugo C.

unread,
Jun 9, 2015, 6:55:54 PM6/9/15
to publice...@googlegroups.com, solv...@gmail.com, vich...@gmail.com
Para mi la potencia de CursorAdaper esta en que es extensible (lo puedes adecuar) por eso de mi interés en las clases que aporta Victor, y la idea es que me sirvan para seguir usando Fox como RAD, pero a mi parecer SPT nunca lo vas dejar de usar.

Saludos

Hugo C.

unread,
Jun 9, 2015, 7:02:46 PM6/9/15
to publice...@googlegroups.com, vich...@gmail.com
.. y con el método CursorDetach lo desligas del objeto cursoradapter.

y después  como lo vuelves a ligar para actualizar ?

Saludos.


El martes, 9 de junio de 2015, 16:53:00 (UTC-6), Víctor Hugo escribió:
Hola tocayo

No tengo ejemplo de eso debido a que recurro al cursor adapter solo cuando debo actualizar un conjunto de registros con altas, bajas y modificaciones, pero es muy sencillo obtener lo que quieres: Una vez creado el objeto cursoradapter, con el método CursorFill traes los datos al cursor especificado por la propiedad Alias y con el método CursorDetach lo desligas del objeto cursoradapter y de esa forma el cursor no se cierra cuando el objeto sea eliminado.

Cursor adapter es sumamente útil para actualizar (grabar, borrar, modificar) tablas tipo detalle, hay un ejemplo de esto en http://www.mediafire.com/download/oglq0a8od3ac9gg/Menus.rar

Saludos,
Víctor.
Lambaré - Paraguay.

Víctor Hugo Espínola Domínguez

unread,
Jun 9, 2015, 7:06:40 PM6/9/15
to publice...@googlegroups.com
CursorAttach

Víctor Hugo Espínola Domínguez

unread,
Jun 9, 2015, 7:11:18 PM6/9/15
to publice...@googlegroups.com
Hola Antonio


>Pero después de ver esos código me sigo quedando con Foxydb. jeje

Esos códigos son la definición de la clase, el uso es bastante simple:

     Procedure InsertarRegistro(tcTabla, tcClave As String, toRegistro As Object) As String
    
     Local loCurAdapter As "vheCaGeneric"
     Local laError[1], lcClave, lcRetorno, lcTabla, llOK
    
     lcTabla      = Alltrim(m.tcTabla)
     lcClave      = Alltrim(m.tcClave)
     loCurAdapter = Createobject("vheCaGeneric", This.uConexion)
     With m.loCurAdapter
         .Alias          = "curCa" + m.lcTabla
         .Datasource     = This.uConexion
         .DataSourceType = This.cTipoDatos
         .Nodata         = .T.
         .SelectCmd      = "Select * From " + m.lcTabla
         .QueryFill()
         .MakeUpdatable(m.lcTabla, m.lcClave, .T.)
     Endwith
    
     Insert Into (m.loCurAdapter.Alias)    ;
         From Name m.toRegistro
    
     llOK = Tableupdate(.T.)
     If m.llOK
         lcRetorno = "OK" + Transform(This.UltIdGenerado(This.uConexion, m.loCurAdapter))
     Else
         Aerror(laError)
         lcRetorno = "ERROR en grabar tabla: " + m.lcTabla + m.laError[2]
     Endif
        
     Use In Select(m.loCurAdapter.Alias)
    
     Return (m.lcRetorno)
    
     Endproc


Saludos,
Víctor.
Lambaré - Paraguay.

Hugo C.

unread,
Jun 9, 2015, 8:00:33 PM6/9/15
to publice...@googlegroups.com, vich...@gmail.com
Siguen las pruebas, Gracias. .

Saludos.


El martes, 9 de junio de 2015, 17:06:40 (UTC-6), Víctor Hugo escribió:
CursorAttach

Hugo C.

unread,
Jun 9, 2015, 11:05:26 PM6/9/15
to publice...@googlegroups.com, vich...@gmail.com
Victor, esto que muestras es la idea, pero en un modo desconectado.

Saludos.

Víctor Hugo Espínola Domínguez

unread,
Jun 9, 2015, 11:27:38 PM6/9/15
to publice...@googlegroups.com
Hola tocayo

De hecho eso trabaja en modo desconectado, recién cuando se crea el objeto de la capa de datos se establece la conexión y se crea el cursor adapter.

Mi clase de capa de datos actualmente trabaja en el modo conectar/desconectar, recién ahora me surge la necesidad de que trabaje con una conexión persistente y la voy a implementar en cuanto pueda.

Saludos,
Víctor.
Lambaré - Paraguay.

Jorge Gonzalez

unread,
Jun 10, 2015, 7:13:50 AM6/10/15
to publicesvfoxpro@googlegroups com
Buen día compañeros del foro.
Ante todo agradezco todas las intervenciones. La verdad que el zorro muestra mucha flexibilidad en un amplio campo. Con todos los años que tengo usándolo no deja de sorprenderme y mejor aún todavía sigo aprendiendo.
Cuando conocí los Cursoradapter fue una revolución para mi, mucho dolores de cabeza se me quitaron, pero me ha encantado el manejo de capaz como lo propone Fernando, mi idea es encapsular o unir filosofías de trabajo. Usar las ideas de  Rafael Copquin (no he leído de Cetin Basoz) mas las de Fernando
Pero ahora tengo mas dudas:
Yo pensaba que el cursoradapter se desconectaba de manera automática del servidor. O dicho de otra manera, que no se mantenía conectado a la base de datos, ya que, el cursoradapter es una tabla física local, es lo que pienso.
si voy a trabajar con PostgreSql, la idea es usar un ambiente cliente/servidor, que solo se conecte al servidor solo para tomar datos y para actualizar tablas. 
Entonces manejos mis cursores hechos con cursoradapter y los manejo de manera local
Cuando mandamos el comando =tableupdate() este se conecta automáticamente o hay que conectarlo previamente?

Por otro lado
En los formularios de facturación debo dar la posibilidad de tener registros en espera para facturación. Porque se puede dar el caso de que un cliente quiera agregar un producto y mientras lo busca el facturador atienda al siguiente de turno, que ideas me pueden dar para esto?
--

Víctor Hugo Espínola Domínguez

unread,
Jun 10, 2015, 1:34:48 PM6/10/15
to publice...@googlegroups.com
Hola Jorge


>Yo pensaba que el cursoradapter se desconectaba de manera automática del servidor. O dicho de otra manera, que no se mantenía >conectado a la base de datos, ya que, el cursoradapter es una tabla física local, es lo que pienso.
La conexión al servidor es idependiente del objeto cursor adapter (CA), generalmente se establece primeramente la conexión luego se crea el CA  y con la propiedad DataSource se le indica la conexión a ser usada. También se puede crear el CA sin que haya una conexión activa y en ese caso se asigna al DataSource la función que conecta al servidor, la clase CA no tiene un método para desconectarse del servidor, para eso está la función VFP SqlDisconnect.
Otro tema es la persistencia del cursor creado por CA, si el objeto es reasignado o liberado (Release) entonces dicho cursor desaparece!, si se desea conservarlo entonces antes de liberar el CA se debe ejecutar el método CursorDetach.
Sí es cierto que el CA "se desconecta automáticamente" en el sentido de que si traes datos del servidor mediante CA y posteriormente la tabla origen es modificada por alguien esos cambios no se reflejan en el cursor del CA.


>si voy a trabajar con PostgreSql, la idea es usar un ambiente cliente/servidor, que solo se conecte al servidor solo para tomar datos y >para actualizar tablas. 
Esa es una responsabilidad de la capa de datos, tú eliges si la conexión será permanente u ocasional dependiendo del tipo de aplicación a desarrollar, no tiene niguna relación con CA. Si vas aprogramar cliente/servidor en 3 capas el CA debe ser usado exclusivamente en la capa de datos, lo ideal es que uses tanto CA como SPT de acuerdo a la conveniencia de cada caso.

>Entonces manejos mis cursores hechos con cursoradapter y los manejo de manera local
Los datos que usarás en la capa de interfase (UI) pueden ser cursores, objetos, arrays, colecciones, es tu elección; qué y de cual tipo de datos se  lo pides a la capa de negocios (BO), la UI recibe los datos de la BO y los convierte al formato adecuado si es necesario. La UI y la BO no saben ni les interesa con qué técnica se obtuvieron los datos desde el servidor!


>Cuando mandamos el comando =tableupdate() este se conecta automáticamente o hay que conectarlo previamente?
En la programación en capas se supone que no hay acople entre ellas, es decir el cursor creado en la capa de datos no debería existir en la BO ni en la UI.
La estategia es la siguiente:
En la UI los datos están listos para su envío al servidor, los envia a la capa de negocios la cual previa validación los envía a la capa de datos y en esta se crea el CA y los datos recibidos se vuelcan al cursor del CA para luego llamar a la función TableUpdate.

Saludos,
Víctor.
Lambaré - Paraguay.

Hugo C.

unread,
Jun 10, 2015, 1:46:38 PM6/10/15
to publice...@googlegroups.com, vich...@gmail.com
Ok Victor, voy a revisarlo bien, aún no he tenido tempo completo.

Saludos

Jorge Gonzalez

unread,
Jun 10, 2015, 11:10:14 PM6/10/15
to publicesvfoxpro@googlegroups com
Amigos del foro. Sigo programando en capas y uniendo las metodologías de Fernando junto con la de  Rafael Copquin y para ser preciso la que Victor Hugo adaptó.
El resultado va por los momentos de la siguiente forma:
Main.prg
*--MAIN.PRG
PUBLIC oPob
LOCAL loEx AS Exception, lcMsg
STORE "" TO lcMsg
oPob=NEWOBJECT("cl_app_bus","INICIALIZA.PRG")
oPob.CargarSet()
oPob.CargaPVar()
DO FORM formMain

INICIALIZA.PRG
*-- INCIALIZA.PRG
DEFINE CLASS cl_app_bus AS Custom
PROCEDURE CargarSet
  SET REPROCES TO 0 automatic
  SET CENTURY OFF
  SET CENTURY TO 19 ROLLOVER 60
  SET DATE TO DMY
  SET DELETED ON
  SET ECHO OFF
  SET EXCLUSIVE OFF
  SET POINT TO "."
  SET SAFETY OFF
  SET SEPARATOR TO ","
  SET SYSMENU OFF
  SET STATUS BAR OFF
  SET TALK OFF
  SET NOTIFY OFF
ENDPROC

PROCEDURE CargaPVar()
  PUBLIC ConPostGresql
ENDPROC

PROCEDURE CargaMenu
  DO mSmarket.mpr
ENDPROC

PROCEDURE psql_connect
  LOCAL miErrorTry, loEx AS Exception, lcMsg
  STORE "" TO lcMsg
  TRY
    lcConc = "Driver={PostgreSQL ODBC Driver(ANSI)};Server=192.168.1.106;Port=5432;Database=smarket;Uid=postgres;Pwd=satriani"
    miErrorTry = .F.

    *** Genera la conexión
    STORE SQLSTRINGCONNECT(lcConC,.T.) TO ConPostGresql
  CATCH
    lcMsg=loEx.Message     
    miErrorTry = .T.  
  ENDTRY

  *** Verifica si existe un error en la conexión
  IF miErrorTry THEN  
    MESSAGEBOX(lcMsg,"Error en conexión")
    RETURN
  ENDIF
ENDPROC

ENDDEFINE

Diseñé un formulario llamado fProductos para la consulta y carga de los mismos, está muy básico la idea es encapsular y seguir el modelo de capas, pero aqui se puede observar mejor el trabajo en capas y cursoradapter, en el caso particular de los procedure de la clase micursoradapter de Victor Hugo 
En el formulario definí una propiedad titulada oDatos y en el load coloqué:
THISFORM.oDatos=NEWOBJECT('cl_productos','Productos_bus.prg')

Que es el llamado a la capa de negocio específica para este formulario Productos_bus
*--Productos_bus.prg
DEFINE CLASS cl_productos AS Custom 
  oDb=NULL
  oDatos=NULL
  c_prod_codbar=""
  c_pro_nombre=""
  
  PROCEDURE INIT
    THIS.oDb=NEWOBJECT('cl_prod_db','productos_db.prg')
    THIS.SolicitaCAdapter()
    THIS.SolicitaCursores()
  ENDPROC

  PROCEDURE SolicitaCAdapter
    This.oDatos=this.CreaAdapters_db()
  ENDPROC
  
  PROCEDURE CreaAdapters_db
    RETURN THIS.oDb.CreaAdaptersProductos()
  ENDPROC
    
  PROCEDURE SolicitaCursores
    RETURN THIS.oDb.CreaCursores()
  ENDPROC
ENDDEFINE

Capa de datos productos_db
*-- productos_db
DEFINE CLASS cl_prod_db AS Custom 
  lodblist=NULL
  lodbvacia=NULL

  PROCEDURE CreaAdaptersProductos
    this.lodbList= NEWOBJECT('cl_cfrom_pgsql','cfpgsql_bus.prg') && capa negocio general
    this.lodbvacia= NEWOBJECT('cl_cfrom_pgsql','cfpgsql_bus.prg') && capa negocio general
    lcWhereResgrid=""
    TEXT TO lcSelectResGrid NOSHOW PRETEXT 15
      select PRODUCTOS.PRO_ID, PRO_NOMBRE from PRODUCTOS
    ENDTEXT
    this.lodbList.CreaCAdapter('productos','caProdList','pro_id',.F.,.F.,lcSelectResGrid + lcWhereResgrid)   
    
    lcWhereResgrid=" where 2=1"
    TEXT TO lcSelectResGrid NOSHOW PRETEXT 15
      select PRODUCTOS.PRO_ID, PRO_NOMBRE from PRODUCTOS
    ENDTEXT
    this.lodbvacia.CreaCAdapter('productos','caProductos','pro_id',.T.,.F.,lcSelectResGrid + lcWhereResgrid)
  ENDPROC
  
  PROCEDURE CreaCursores
      CREATE CURSOR c_ListaProd(pro_id int, pro_nombre c(100))
      this.cargaregistros()
  ENDPROC
  
  PROCEDURE cargaregistros
    SELECT caProdList
    SCAN
      INSERT INTO c_ListaProd(pro_id, pro_nombre) VALUES(caProdList.pro_id, caProdList.pro_nombre)
    ENDSCAN
  ENDPROC  
  
ENDDEFINE

Capa de negocio cfpgsql_bus.prg
*--cfpgsql_bus.prg
DEFINE CLASS cl_cfrom_pgsql AS Custom 
o_DB=NULL
oDatos=NULL

  PROCEDURE INIT
    This.o_db=NEWOBJECT('MiCursorAdapter',"micursoradapter.prg")
  ENDPROC

  PROCEDURE DevolverCAdapter
    PARAMETERS cTable, cAlias, cKeyField, lUpdata, lEmpty, cSelectCmd
    RETURN this.o_db.CrearCursor(cTable, cAlias, cKeyField, lUpdata, lEmpty, cSelectCmd)
  ENDPROC

  PROCEDURE CreaCAdapter
    PARAMETERS cTable, cAlias, cKeyField, lUpdata, lEmpty, cSelectCmd
    THIS.oDatos=THIS.DevolverCAdapter(cTable, cAlias, cKeyField, lUpdata, lEmpty, cSelectCmd)
  ENDPROC
  
ENDDEFINE

En esta capa es que se instancia los procedure de micursoradapter. Como este proyecto siempre se usará ODBC, quité lo que tiene que ver con Native. Tengo una copia del original tal como lo hizo Victor Hugo.
Y finalmente los procedures de micursoradapter. No voy a copiar todos los procedure, solo el inicial que con eso se tiene una idea:

*-- MiCursorAdapter
*--------------------------------------------------------------------------------------------------------------------------
*
* Esta clase es una adaptación de la publicada por Rafael Copquin
*
*--------------------------------------------------------------------------------------------------------------------------
*
DEFINE CLASS MiCursorAdapter AS CURSORADAPTER

cConexion = ""
cKeyField = ""
cSch      = ""
cUnl      = ""
cUfl      = ""

ALIAS              = ""
DATASOURCETYPE     = "ODBC"
SELECTCMD          = ""
KEYFIELDLIST       = ""
UPDATABLEFIELDLIST = ""
UPDATENAMELIST     = ""
TABLES             = ""
BUFFERMODEOVERRIDE = 5
*
*--------------------------------------------------------------------------------------------
*
FUNCTION INIT
LPARAMETERS tcDataSourceType, tuConexion

    LOCAL miErrorTry
    TRY
      lcConc = "Driver={PostgreSQL ODBC Driver(ANSI)};Server=192.168.1.106;Port=5432;Database=smarket;Uid=postgres;Pwd=satriani"
      miErrorTry = .F.

       *** Genera la conexión
      STORE SQLSTRINGCONNECT(lcConC,.T.) TO ConPostGresql
      CATCH
         miErrorTry = .T.  
    ENDTRY

    *** Verifica si existe un error en la conexión
    IF miErrorTry THEN  
      MESSAGEBOX("Ocurrio un error en la conexion a POSTGRESQL")
      RETURN
    ELSE
THIS.DATASOURCE = ConPostGresql
    ENDIF


ENDFUNC
*
*--------------------------------------------------------------------------------------------
*
PROCEDURE CrearCursor
LPARAMETERS tcTable, tcAlias, tcKeyField, tlUpdatable, tlEmpty, tcSelectCmd, tcTableList, tcNoActCampos
LOCAL cSchema, cUfl, cUnl, lOK, cSelectCmd, cNoActCampos

USE IN SELECT( tcAlias )

lOK = .T.
    IF VARTYPE( tcKeyField ) = "C"
THIS.cKeyField = tcKeyField
ELSE
MESSAGEBOX( "tcKeyField NO especificado!:-( " )
CANCEL
ENDIF

IF VARTYPE( tcNoActCampos ) = "C"
cNoActCampos = UPPER( tcNoActCampos )
ELSE
cNoActCampos = "***"
ENDIF

* try to generate the cursor schema, analyzing the tables as per the SQL statement

TRY
    IF VARTYPE( tcSelectCmd ) = "C"       && use the Select statement if passed as a parameter
cSelectCmd = tcSelectCmd
THIS.MakeSchema( tcTable, cSelectCmd, tcAlias, cNoActCampos )
ELSE
cSelectCmd = [select * from ] + tcTable
THIS.MakeSchema( tcTable, '', tcAlias, cNoActCampos )
ENDIF

CATCH TO oException
lOK = .F.
MESSAGEBOX("Cursor "+tcAlias+" no se pudo generar "+CHR(13)+oException.MESSAGE,"Error de sistema")

ENDTRY

IF lOK = .F.
RETURN
ENDIF


cUfl    = THIS.cUfl                     && updatable field list
cUnl    = THIS.cUnl         && update name list
cSchema = THIS.cSch   && table schema

THIS.CLOSETABLE( tcAlias )   && close the cursor before building the CA

lOK = .T.

TRY
IF VARTYPE( tcSelectCmd ) <> "C"   && if we do not pass our own select statement
IF tlEmpty = .T.
** build an empty cursor
cSelectCmd = [select * from ] + tcTable + [ where 1 = 0 ]
ELSE
** bring all records
cSelectCmd = [select * from ] + tcTable
ENDIF
ELSE
cSelectCmd = tcSelectCmd    && use the select statement passed as a parameter
ENDIF
WITH THIS
.ALIAS        = tcAlias
IF VARTYPE( tcTableList ) = "C" AND LEN( ALLTRIM( tcTableList ) ) > 0
.TABLES         = tcTableList
ELSE
.TABLES = tcTable
ENDIF
.KEYFIELDLIST       = .cKeyField
.SENDUPDATES    = tlUpdatable
.SELECTCMD        = cSelectCmd
.UPDATABLEFIELDLIST = cUfl
.UPDATENAMELIST = cUnl
.CURSORSCHEMA       = cSchema
.NODATA = tlEmpty
.ALLOWINSERT        = .T.
.ALLOWUPDATE        = .T.
.ALLOWDELETE        = .T.
.CURSORFILL()
ENDWITH
SELECT ( tcAlias )

CATCH TO oErrors
MESSAGEBOX("Cursor "+tcAlias+" was not generated"+CHR(13)+oErrors.MESSAGE;
,16,"Attention")
lOK = .F.

ENDTRY

RETURN lOK
ENDPROC

Ya saben que estoy abierto a toda sugerencia.
Saludos
--

Jorge Gonzalez

unread,
Jun 10, 2015, 11:10:49 PM6/10/15
to publicesvfoxpro@googlegroups com
Se me olvidó decirles que todo funciona sin problemas.
--

Víctor Hugo Espínola Domínguez

unread,
Jun 10, 2015, 11:43:25 PM6/10/15
to publice...@googlegroups.com
Hola Jorge

Puedes enviarnos el proyecto para que lo probemos?


Saludos,
Víctor.
Lambaré - Paraguay.

Jorge Gonzalez

unread,
Jun 11, 2015, 8:02:54 AM6/11/15
to publicesvfoxpro@googlegroups com
Claro que si, aunque, soy nuevo en Postgresql y no se como trasladar una base de datos de un servidor a otro. El PGAdmin III tiene una opción de backup, será simplemente con eso?
--

Jorge Gonzalez

unread,
Jun 11, 2015, 8:10:01 AM6/11/15
to publicesvfoxpro@googlegroups com
Me dice que necesito msxml4 SP1. 
Es una aplicación para crear menú, por lo que veo
--

Víctor Hugo Espínola Domínguez

unread,
Jun 11, 2015, 11:37:21 AM6/11/15
to publice...@googlegroups.com
Hola Jorge

No es necesario que envies la base de datos, la crearé en Sqlserver. Envía el proyector solamente.

Saludos,
Víctor.
Lambaré - Paraguay.

Antonio Meza

unread,
Jun 11, 2015, 11:39:42 AM6/11/15
to publice...@googlegroups.com, sop...@avdelsistemas.com
Unas preguntas!!

El código que publicaste esta fácil de digerir, pero tengo una duda en la función para generar el CursorAdapter, en la que se llama CrearCursor veo que recibe muchos parámetros, mi pregunta es por cada tabla que necesites es necesario especificar todos ellos?

PROCEDURE CrearCursor
LPARAMETERS tcTable, tcAlias, tcKeyField, tlUpdatable, tlEmpty, tcSelectCmd, tcTableList, tcNoActCampos

Y si la tabla que se requiere es resultado de dos o mas tablas es decir usando Inner Join por ejemplo, cuando se vayan a actualizar los cambios en la tabla principal hay que especificar los campos que se van a actualizar o todo eso es automático?

saludos
Antonio Meza

Daniel Sánchez

unread,
Jun 11, 2015, 12:55:04 PM6/11/15
to Comunidad de Visual Foxpro en Español
Un cursoradapter, se encarga de actualizar y trabajar con una tabla, si es por supuesto indicado que sea un cursor actualizable, si es así y realizas una consulta obteniendo datos (campos) de otras tablas también, el cursoradapter al momento de tu indicarle que actualice los datos de dicha tabla te arrojará error, ya que creara un update a todos los campos que ha traído tu select, el no sabe que campos no le pertenecen a la tabla principal (indicada en el from del select), por lo que al enviar el update con campos que no le pertenece a dicha tabla pues arrojará un error ya que esos campos no existen en la tabla, te pongo un ejemplo:

Select cliente.codigo, cliente.razonsocial, cliente.direccion, vendedor.nombres, zonas.descripcion
from cliente
inner join vendedor on clientes.vendedor=vendedor.codigo
inner join zonas on clientes.zona=zonas.codigo
where razonsocial like '%distribuidora%'

con esta consulta obtendrías una tabla con los siguientes campos (supongo que ya sabes cuales son pero los pongo) y suponiendo que mi cursor se llame clte.

clte.codigo, clte.razonsocial, clte.direccion, clte.nombres, clte.descripcion

como ves el nuevo cursor tiene todos los campos resultado de la consulta realizada, entonces si tu realizas un cambio en alguno de los campos el cursoradapter generara un update por los campos de la tabla o cursor generado

update cliente set codigo=losdatos, razonsocial=losdatos, direccion=losdatos, nombres=losdatos, descripcion=losdatos
where cliente.codigo=elcodigo

donde losdatos son los datos correspondientes a cada campo a actualizar, como ves el update tratará de realizar los update de campos no existentes en la tabla cliente como son nombres y descripción, por lo que devolverá un código de error de parte del servidor, ahora esta situación es muy común, así que al cursoradapter debes indicarle cuales son los campos que no debe considerar para la actualización que en este caso seria nombres y descripción.

Ahora si te das cuenta la instrucción upate NUNCA va ha actualizar campos de varias tablas a la vez, un update una tabla, igual que el cursoradapter, una tabla un CA.

Por eso el CA debe crearse una nueva clase en base al CA para que considere los campos a no actualizar y otros detalles más, al menos así lo uso.

Saludos

--
Daniel Sánchez Escobar
Investigación y Desarrollo
Reset Software & Sistemas
Móvil +051-949398047 RPM #948615385
Trujillo - Perú

P  Sugerimos no imprimir este e-mail a menos que sea absolutamente necesario. Protejamos el medio ambiente.

Víctor Hugo Espínola Domínguez

unread,
Jun 11, 2015, 1:22:49 PM6/11/15
to publice...@googlegroups.com
Hola Antonio

Nunca probé si se puede o no actualizar varias tablas, me llama la atención y me surgen dudas en la ayuda ayuda de la propiedad Tables.

La clase hecha por Rafael Copquin actualiza solo la tabla principal y no importa si el cursor contiene campos de otras tablas, una explicación detallada de la clase puedes verla aquí: https://www.universalthread.com/ViewPageArticle.aspx?ID=830

El cursor adapter es una maravilla pero está más orientada a la programación monolítica donde todo el manejo de datos se hace en el formulario, incluso tiene el builder incorporado a la data environment.
Con la programación en capas es útil cuando se necesita efectuar una actualización masiva en una tabla y si se desea aprovechar toda su potencia debe usarse con la clase XmlAdapter, con esta técnica la capa de datos recibe un xml (Diffgram) que contiene los cambios hechos al cursor original y solamente esos se transfieren vía CA al servidor.

Saludos,
Víctor.
Lambaré - Paraguay.

Antonio Meza

unread,
Jun 11, 2015, 1:46:24 PM6/11/15
to publice...@googlegroups.com, resets...@gmail.com
Gracias Daniel por la explicación bien detallada!!

Era lo que imaginaba que hay que hacer muchas cosas manuales, que me parecen un poco innecesarias y que bien podrían ver desarrollado para que el propio CursorAdapter lo resolviera.

Y en el caso de las fechas vacías también se tiene que resolver manual o el cursorAdapter soluciona el problema, por ejemplo FireBird no acepta guardar un campo Date vació, debe ser mínimo Null si no mal recuerdo, y en el caso de MariaDB y Mysql tampoco permiten valores vacíos en los campos Date puedes guardarlo como 000-00-00, en cursorAdapter como se soluciona eso?

El problema de los cursores actualizables es que requieres tener conexión permanente al servidor, o en el caso de CursorAdapter esto lo maneja diferente que te puedes desconectar y luego actualizar el cursor al servidor? y que sepa que cambios realizaste o tienes que indicarle que a partir de un cursor u objeto de datos actualice el servidor, lo que si tienes 30 registros y solo modificaste un campo de un registro tenga que eliminar todos en el servidor e insertar todos de nuevo con el cambio?

A pesar de que en teoría CursorAdapter parece ser muy potente hay muchas cosas que se tienen que realizar de forma manual, a diferencia de usar SPT que te permite aprovechar las ventajas de VFP y las ventajas del Servidor, y una clase bien estructurada como FoxyDb simplifica Enormemente el acceso a datos y utilizarlo como una capa en sustitución de CursorAdapter veo que el código seria aun mas simple.

Desconozco todas las ventajas de CursorAdapter porque como eh comentado nunca lo use por los comentarios que vi de varios Gurus de VFP, pero sigo viendo que hay que hacer muchas cosas manuales que de forma muy sencilla se pueden automatizar pero que no lo hace el CursorAdapter lo debe realizar uno.

Ejemplo un cursor que involucra varias tablas, tan sencillo en FoxyDB, traigo los campos de la tabla a actualizar, verifico que el campo modificado pertenezca a la tabla y voy armando el comando SQL correspondiente, eso mismo pudieron haber echo de forma automática en CursorAdapter. Ejemplo de FoxyDb.

txtSql  = "Select cliente.codigo, cliente.razonsocial, cliente.direccion, vendedor.nombres, zonas.descripcion
from cliente
inner join vendedor on clientes.vendedor=vendedor.codigo
inner join zonas on clientes.zona=zonas.codigo
where razonsocial like '%distribuidora%' "

oDb.Query(txtSql,"Clientes")  && Obtener el Cursor
oDb.CursorEdit,"Clientes")     && Aplicar Buffering 5 al cursor
replace clientes.codigo with "000001"
replace clientes.nombres with "PRUEBA"
If oDb.Update("Clientes")       && Enviar instrucción SQL al servidor e iniciar transacción
     oDb.Commit()                    && Confirmar transacción
ENDIF

Otra cosa por ejemplo cada servidor de base de datos maneja los campos Autoincrementables de diferente forma, no hay un estandar SQL para obtenerlo, entonces como se obtiene en CursorAdapter este valor?

saludos
Antonio Meza


Antonio Meza

unread,
Jun 11, 2015, 2:02:42 PM6/11/15
to publice...@googlegroups.com, vich...@gmail.com
Hola Victor!!

No me refiero a actualizar todas las tablas, me refiero a actualizar solo una tabla que involucra varias tablas (consulta), estaría de locos tener que actualizar varias tablas que están dentro de una consulta.

Si he leído mucho sobre CursorAdapter pero insisto muchas cosas son manuales al final terminas complementándolo lo que a mi parecer pierde beneficio, y su manejo no es fácil si quieres hacerlo por programación y no usar los asistentes.

FoxyDb de manera natural ya tienes una capa de acceso a datos, la puedes ajustar a tu capa ya existente porque son funciones ya programadas que solo tienes que reutilizar en una capa de datos, lo mismo que haces con CursorAdapter pero mas fácil ,mas control y menos código.

Por eso estoy preguntando detalles a los que lo usan CA para conocer mas a fondo y luego no decir burradas jajaja

saludos
Antonio Meza

Daniel Sánchez

unread,
Jun 11, 2015, 2:04:36 PM6/11/15
to Comunidad de Visual Foxpro en Español
En cuanto al tema de trabajar en modo desconectado en cualquiera de los casos puedes trabajarlo, ya sea por SPT, CA o FoxyDB, esto se puede ya que siempre en una copia la que se trae del servidor y una vez traída dicha copia termina el enlace, no es necesario que exista una conexión activa, si deseo actualizar simplemente vuelvo a conectar y se envía el comando de update ya sea con SPT, CA o FoxyDB y si deseo vuelvo a desconectar.

En cuanto a los valores almacenar en la tabla como campos fechas, todo depende de como le has indicado que tome los valores predeterminados en la tabla en tu BD en tu motor SQL, ahora eso no lo analiza ni lo determina en el SPT, el CA o FoxyDB (debo suponer que es el mismo para el), sino el driver ODBC es el que se encarga de realizar el traspaso de los datos, en este caso cuando realizamos una consulta, la mayoría de las BD almacenan la fecha como una cadena de carácter, pero cuando lo trae en un cursor no los devuelve como un formato fecha, igual para el campo bit (en SQLServer) que cuando llega a VFP lo toma como lógico.

En cuanto a realizar cosas manuales que el CA no ha venido de diseño, pues fácilmente como comente creas la clase en base al CA que resuelva lo que necesites y ya, simplemente instancias a la nueva clase que ya resuelve su falencias y trabajas con ella.

Saludos

Daniel Sánchez

unread,
Jun 11, 2015, 2:23:41 PM6/11/15
to Comunidad de Visual Foxpro en Español
Yo trabajo con una clase de la siguiente manera, así defino mi CA para trabajar con una tabla actualizable

CCADENASQL = 'select ventasitem.*, maestro.ubicacion, catalogo.marca'+;
' from ventasitem '+;
' left join catalogo on catalogo.rubro=ventasitem.rubro and catalogo.srubro=ventasitem.srubro and '+;
' catalogo.codigo=ventasitem.codigo '+;
' left join maestro on maestro.rubro=ventasitem.rubro and '+;
' maestro.srubro=ventasitem.srubro and maestro.codigo=ventasitem.codigo and '+;
' maestro.almacen=10'+;
' where 2=1'
This.MANAGERDATA.INICIALIZAR(HANDLESQL, "ventasitem", "vtasi")
This.MANAGERDATA.CMDSQL(ccadenasql)
This.MANAGERDATA.FIELDUPDATE()
this.managerdata.fieldnoupdate=[ubicacion, marca]
This.MANAGERDATA.FetchSize = -1
This.MANAGERDATA.IDKEY('secuencia, keyitem, codigo, rubro, srubro')
If  .Not. This.MANAGERDATA.RUNCMDSQL()
= ERRORDATA("Iniciando items de ventas")
Endif

este una tabla que no deseo que se actualice solo para consulta

This.AddObject("malmacen", "managerdata")
This.MALMACEN.INICIALIZAR(HANDLESQL, "almacen", "talmacen")
This.MALMACEN.CMDSQL("select almacen.* from almacen where estado=0")
This.MALMACEN.SendUpdates = .F.
If  .Not. This.MALMACEN.RUNCMDSQL()
= ERRORDATA("cargando almacen")
Endif 


Ya con eso con el cursor obtenido insertas, remplazas o eliminas todo como simples comandos vfp.

Saludos

Antonio Meza

unread,
Jun 11, 2015, 3:19:09 PM6/11/15
to publice...@googlegroups.com, resets...@gmail.com
Hola Daniel!!

Me explico mejor, generar un cursor Actualizable implica mantener la conexión al servidor para poder luego enviar un simple TableUpdate, esto lo se porque con DBVP así trabajaba, y si perdías conexión pues se perdía la relación entre el cursor actualizable y el Handle de conexión y si conectaba de nuevo obtenía otro Handle pero ya el tableUpdate marcaba error.

Ahora en CA eh leído que permite generar cursores actualizables también, mi duda es si trabajan igual que hacerlo manual como comente arriba o si CA te permite desconectarte y al conectarte enviar los cambios con un simple tableUpdate? lo has probado realmente o solo crees que si funciona, se que hay unas propiedades de CA que te permite hacer el cursor actualizable pero no se si se comportan como explique arriba.

No confundir con obtener un cursor y luego enviar los cambios por CA, son 2 cosas muy diferentes. Un cursor actualizable solo tienes que aplicar un TableUpdate y se almacenan en el servidor los cambios locales del cursor, sin hacer ninguna otra cosa

En cuanto a los campos DATE el driver ODBC te ayuda cuando consultas u obtienes un cursor, incluso usando Cursores Actualizables el driver ODBC se encarga de hacer la aplicación, al menos eso lo vi con DBVFP, pero con SPT lo tienes que manejar manual porque tu envías de retorno la instrucción SQL, por eso mi duda con CA y cursores desconectados.

FoxyDB usa al 100% SPT por lo tanto son lo mismo solo que con funciones mas sencillas y fácil de usar, que aplicar SPT de forma manual.

saludos
Antonio Meza

Antonio Meza

unread,
Jun 11, 2015, 3:29:21 PM6/11/15
to publice...@googlegroups.com, resets...@gmail.com
Hola Daniel!!

Es justo lo que comento, es mas código para realizar la misma acción, es decir en CA ocupas mas cosas manuales que con SPT puedes hacer mas sencillo y las ventajas entre CA y SPT son mínimas realmente después de leer lo que han explicado.

Por ejemplo con esa forma de usas para obtener el cursor actualizable, como le haces para que se guarden los cambios en el servidor? envías un simple TableUpdate() o que pasos tienes que hacer? y si te desconectas son los mismos pasos?

En FoxyDb para obtener un cursor usando el mismo ejemplo que el tuyo que no vas a modificar es muy sencillo

If oDb.Sql("select almacen.* from almacen where estado=0","talmacen")
     wait windows "Error al Obtener Cursor: " + str(oDb.error_Code)
endif

la diferencia de código es mucha y entre menos código mas sencillo de deputar y entender.

saludos
Antonio Meza

Víctor Hugo Espínola Domínguez

unread,
Jun 11, 2015, 4:50:34 PM6/11/15
to publice...@googlegroups.com
Hola Antonio


>No me refiero a actualizar todas las tablas, me refiero a actualizar solo una tabla que involucra varias tablas (consulta), estaría de locos >tener que actualizar varias tablas que están dentro de una consulta.
Aparentemente se puede por la sintaxis de la propiedad UpdateNameList, pero no encontré ningún ejemplo y nunca lo probé.

>Si he leído mucho sobre CursorAdapter pero insisto muchas cosas son manuales al final terminas complementándolo lo que a mi >parecer pierde beneficio, y su manejo no es fácil si quieres hacerlo por programación y no usar los asistentes.
El que decide usar CA crea una clase que simplifica su uso..

>FoxyDb de manera natural ya tienes una capa de acceso a datos, la puedes ajustar a tu capa ya existente porque son funciones ya >programadas que solo tienes que reutilizar en una capa de datos, lo mismo que haces con CursorAdapter pero mas fácil ,mas control y >menos código.
Es que no se debería usar ninguna clase de VFP sin previamente fabricarle un wrapper.

Un ejemplo de guardar solo los cambios de un conjunto de registros, usando CA original:

Procedure ActualizaOrdenes(tcDiffgr As String, tcIdCliente As String )

Local loCaOrdenes As CursorAdapter
Local loDatos As "Datos" Of "CapaDatos.prg"
Local loXDiffGram As Xmladapter
Local lnOK

Set Multilocks On

loDatos = Newobject("Datos", "CapaDatos.prg")

loCaOrdenes = Createobject("CursorAdapter")
With m.loCaOrdenes
    .Alias              = "curOrdenes"
    .Datasource         = m.loDatos.DS
    .DataSourceType     = m.loDatos.DST
    .SelectCmd          = "Select OrderID, Shipname, ShipAddress, ShippedDate, " ;
                        + "ShipCity, ShipCountry, OrderDate from Orders " ;
                        + "Where CustomerId = '" + Alltrim(m.tcIdCliente) + "'"
    .Tables                = "Orders"
    .KeyFieldList       = "OrderID"
    .UpdatableFieldList = "Shipname, ShipAddress, ShippedDate, ShipCity, ShipCountry, OrderDate"
    .UpdateNameList     = "OrderID Orders.OrderID, Shipname Orders.Shipname, ShipAddress Orders.ShipAddress, " ;
                        + "ShippedDate Orders.ShippedDate, ShipCity Orders.ShipCity, " ;
                        + "ShipCountry Orders.ShipCountry, OrderDate Orders.OrderDate"
    .CursorFill()
Endwith
Select Ordenes
CursorSetProp("Buffering", 5)
loXDiffGram = Createobject("XMLAdapter")
m.loXDiffGram.LoadXML(m.tcDiffgr)
loXDiffGram.Tables.Item(1).Alias = "curOrdenes"
m.loXDiffGram.Tables.Item(1).ApplyDiffgram()

Select curOrdenes

lnOK = Tableupdate(.T.)

Endproc

Puedes ver que se usaron solo 8 de las 76 propiedades y un método, a continuación el mismo ejemplo usando el wrapper vheCaGeneric:

Procedure ActualizaOrdenes(tcDiffgr As String, tcIdCliente As String )

Local loCaOrders  As CursorAdapter
Local loDatos     As "datos" Of "capadatos.prg"
Local loXDiffGram As Xmladapter
Local lnOK

Set Multilocks On

Set Procedure To vheCaGeneric.prg Additive
loDatos = Newobject("Datos", "CapaDatos.prg")

loCaOrders = Createobject("caGeneric", m.loDatos.DS )
With m.loCaOrders
    .Alias     = "curOrdenes"
    .Nodata    = .F.
    .SelectCmd = "Select OrderID, Shipname, ShipAddress, ShippedDate, ShipCity, " ;
               + "ShipCountry, OrderDate from Orders " ;
               + "Where CustomerId = '" + Alltrim(m.tcIdCliente) + "'"
    .QueryFill()
    .MakeUpdatable("Orders", "OrderId", .T.)
Endwith
Select curOrdenes
loXDiffGram = Createobject("XMLAdapter")
m.loXDiffGram.LoadXML(m.tcDiffgr)
loXDiffGram.Tables.Item(1).Alias = "curOrdenes"
m.loXDiffGram.Tables.Item(1).ApplyDiffgram()

Select curOrdenes

lnOK = Tableupdate(.T.)

Endproc

Aquí se usaron solamente 3 propiedades y 0 (cero) métodos de la clase CA y 2 métodos del wrapper

Saludos,
Víctor.
Lambaré - Paraguay.

Daniel Sánchez

unread,
Jun 11, 2015, 8:41:26 PM6/11/15
to Comunidad de Visual Foxpro en Español
Así es Antonio M.

if !tableupdate(.t.)
    =errordata("actualizando")
endif 

En cuanto al tema de desconectar y desconectar es algo que debes programarlo en tu clase, no se hace de manera automática, en tu caso debo suponer que tu gestionas ese escenario y lo resuelves cuando se desea actualizar el realizar nuevamente la conexión de manera transparente sin que el usuario se entere o intervenga.

Pero todo ya lo tengo implementado en la clase, parecido a tu FoxyDb, no creo que tan elaborado pero al menos cumple con lo que necesito.

Saludos

Antonio Meza

unread,
Jun 11, 2015, 9:20:13 PM6/11/15
to publice...@googlegroups.com, resets...@gmail.com
Hola Daniel !!

Pero no contestaste mi pregunta jejej

Es decir ya me comentaste que usando tableUpdate(.t.) se actualizan los cambios en el servidor, mi pregunta es si te desconectas y luego te conectas nuevamente enviando el tableUpdate(.t.) el cursor acutalizalble devuelto por CA si se actualiza o tienes que realizar acciones diferentes? y si lo has probado realmente?

saludos 
Antonio Meza

Antonio Meza

unread,
Jun 11, 2015, 9:26:20 PM6/11/15
to publice...@googlegroups.com, vich...@gmail.com
Hola Victor!!

Definitivamente usar SPT o CA debes crear una clase o librería para automatizar el proceso de acceso a datos.

Realmente no estoy viendo una ventaja significativa entre CA y SPT porque como comente hay muchas cosas que debes manejar manual y es lo mismo que con SPT lo tienes que hacer manual todo, pero se me hace mas sencillo con SPT.

Muchas gracias por tomarte el tiempo en contestar y detallar me sirven mucho para comparar algunas cosas de CA con SPT!!

saludos
Antonio Meza 

Víctor Hugo Espínola Domínguez

unread,
Jun 11, 2015, 11:04:22 PM6/11/15
to publice...@googlegroups.com
Hola Antonio


>Realmente no estoy viendo una ventaja significativa entre CA y SPT porque como comente hay muchas cosas que debes manejar >manual y es lo mismo que con SPT lo tienes que hacer manual todo, pero se me hace mas sencillo con SPT.
La única comparación válida entre CA y SPT es que CA tiene 2 letras y SPT ...(Bernard Bout)
Cursor adapter obtiene datos mediante/desde ODBC, OLE DB, XML, DBF y SPT solo ODBC, y CA usa SPT!
CA y SPT son cosas distintas e incomparables, es cierto que traer datos a un cursor con SPT implica menos código que usar CA, entonces lo mejor es usar ambas técnicas SPT para recibir datos desde el servidor y CA para enviar, y si a esto le sumas la clase XmlAdapter para detectar cambios en el cursor ya está todo resuelto con pocas líneas de codificación.

Saludos,
Víctor.
Lambaré - Paraguay.

Carlos Miguel FARIAS

unread,
Jun 12, 2015, 6:46:55 AM6/12/15
to Grupo Fox
Entiendo que CA es más encapsulado. Te permite manejarte en forma más directa con la interfaz gráfica. SPT requiere que vos manejes más cosas, pero si a futuro tienes que migrar a otra herramienta de desarrollo, la lógica de datos con SPT no cambia, pero CA no lo debe tener ningún otro lenguaje instrumentado, salvo objetos similares en Lazarus o Delphi, y posiblemente en .NET
En PHP, por lo que he visto (no estoy actualizado con las últimas versiones) lo mas parecido es SPT, en python, los modelos trabajan diferente.
Concluyo que SPT, en ciertos casos tiene más futuro que CA.
Saludos: Miguel, La Pampa (RA)

mapner

unread,
Jun 12, 2015, 8:39:23 AM6/12/15
to publice...@googlegroups.com, carlosmig...@gmail.com
VFP ha presentado varias alternativas de acceso a datos remotos,

Vistas remotas: un híbrido para los que migraban desde DBFs hacia Cliente / Servidor, es cómodo de definir visualmente pero a la larga es un esquema un tanto rígido.
SPT: API estilo "funcional", en principio solo para conexiones ODBC
CA: API estilo "OOP", más amplitud en tipo de conexión, ODBC, ADO, XML

Creo que lo mejor es hacerse una clase de abstracción de acceso a datos que a su vez encapsule la metodología de acceso (puede ser SPT o CA).

El estilo que yo uso:

oDM = CREATEOBJECT('rdata_adapter')

oDM.addCursor('cli','select * from clientes where id = 1','clientes','*','id')

replace nombre with 'PEPE' in cli

oDM.Save('cli')

* Saludos

Daniel Sánchez

unread,
Jun 12, 2015, 11:11:05 AM6/12/15
to Comunidad de Visual Foxpro en Español
Es correcto Antonio Meza, si te desconectas y luego te conectas y envías un update sin problema se actualizaran los datos en el servidor.
Lo que debes recordar es que el CA se encarga de generar el update correspondiente para enviar al servidor, por eso es lo mismo para el servidor que hayas seguido conectado todo el tiempo o desconectado hasta antes de enviar el update, no hay un enlace continuo con los datos que traes del servidor, lo traes a tu pc (una copia de los datos), y ahí acaba su trabajo del servidor, luego trabajas en tu copia local, insertar, borrar, modificar una vez realizado cualquiera de esas acciones, recién necesitas saber si hay una conexión con el servidor, si no hay que volver a realizar la conexión para enviar el update, insert o delete.

Saludos

Jorge Gonzalez

unread,
Jun 12, 2015, 11:37:40 AM6/12/15
to publicesvfoxpro@googlegroups com
Hola amigos del foro.
He seguido trabajando con mi proyecto y siguiendo las indicaciones de Mr. Victor Hugo y de acuerdo a los resultados voy a responder varias inquietudes

Antonio Meza:
El código que publicaste esta fácil de digerir, pero tengo una duda en la función para generar el CursorAdapter, en la que se llama CrearCursor veo que recibe muchos parámetros, mi pregunta es por cada tabla que necesites es necesario especificar todos ellos?

PROCEDURE CrearCursor
LPARAMETERS tcTable, tcAlias, tcKeyField, tlUpdatable, tlEmpty, tcSelectCmd, tcTableList, tcNoActCampos

Si lees el codigo de Rafael Copquin, puedes mandar aún mas parámetros y son necesarios para crear el cursor según la programación de Mr. Copquin.

Me explico mejor, generar un cursor Actualizable implica mantener la conexión al servidor para poder luego enviar un simple TableUpdate, esto lo se porque con DBVP así trabajaba, y si perdías conexión pues se perdía la relación entre el cursor actualizable y el Handle de conexión y si conectaba de nuevo obtenía otro Handle pero ya el tableUpdate marcaba error.
No es así. VFP genera el CA y luego te puedes desconectar del servidor y trabajar tus datos, cuando vas actualizar la tabla de origen, haces conexión y con un simple =tableupdate() se guardan los cambios, ya sea insertando registro o modificando alguno existente.

En conclusión, pienso que CA está hecho para seguir trabajando el lenguaje VFP con los cursores generados. Es importante manejar tanto SPT como CA, ya que para programar en otros lenguajes el SPT es como un standard por llamarlo de alguna manera. Anteriormente yo trabajaba colocando las tablas en el dataenvironment de los formularios pero mi gran dolor de cabeza era cuando varios usuarios trabajaban el mismo formulario o distintos formularios usando una misma tabla aún teniendo el cuidado de trabajar en buffer de modo optimista, por otro lado la aplicación se volvía muy lenta. Con esto de CA y cursores de consulta ha sido la gran solución. 

Para los que quieran analizar el proyecto aqui les mando los fuentes, en especial para MR. Victor Hugo, y mando la estructura de la tabla. Recuerden que esto está crudo, apenas empiezo
tabla: productos:
pro_id autoinc.
pro_codbar c(18)
pro_ntof c(60)
pro_prec1 n(12,2)
pro_prec2 n(12,2)
pro_prec3 n(12,2)
pro_ubic c(100)
pro_cimg c(100)
pro_ldc int

Tengo un detalle, cuando se activa el form, el registro del cursor de consulta apunta al último registro y tengo que subir con el scroll para ver el primer registro. He colocado un Go TOP en varios sitios probando y nada.

Voy abrir otro post para obtener información de como trabajar las imágenes en tablas, ya que necesito mostrar verduras y hortalizas en el formulario de facturación.


--

fuentesJGonzalez.r_

Víctor Hugo Espínola Domínguez

unread,
Jun 12, 2015, 12:17:24 PM6/12/15
to publice...@googlegroups.com
Hola Jorge

La nomenclatura que usas para los nombres de campos, te la impusieron o es tu elección?, si es tu elección estás a tiempo de cambiarla porque es realmente horrible, está bien en COBOL porque no tienes forma de saber de donde provienen los datos, pero en xBase o SQL es una práctica molesta, me explico:

pro_ntof

"pro_"  Puede siginificar proveedores, productos, provisiones, prontuario, prorrateo, procuraduría y ... ya me cansé ;-), a mí me dice que estás desperdiciando cuatro caracteres inútilmente porque por más que sepa que en este caso "pro_" significa productos "ntof" puede ser sinónimo de NPI o de cualquier otra cosa.

Una nomenclatura más acorde con el lenguaje puede ser: IdProducto, CodBarra, Precio1, Precio2, etc...

Uso:
 lnIdProducto = Productos.IdProducto
curProductos.CodBarra = ""

Considera la posibilidad de descomponer el campo "Ubic" en Local, Estante, Fila, Colimna o algo parecido, de la forma que está es muy sensible a errores de la IST.

Saludos,
Víctor.
Lambaré - Paraguay.


 

Jorge Gonzalez

unread,
Jun 12, 2015, 12:23:11 PM6/12/15
to publicesvfoxpro@googlegroups com
Esta bien. pero que significado tiene el prefijo "ld"?
--

Antonio Meza

unread,
Jun 12, 2015, 12:33:40 PM6/12/15
to publice...@googlegroups.com, vich...@gmail.com
Muy cierto lo que te dice Victor, pero con DBF solo puedes tener 11 caracteres, usando un contenedor DBC podías usar nombres largos, pero que no se te ocurriera desligar la tabla DBF de la DBC porque pierdes los nombres largos, un fastidio.

Pero usando un servidor de base de datos lo correcto es usar nombres largos que identifiquen los campos claramente, aquí ya no hay escusa para usar nombres cortos difíciles de identificar.

En general uso nombres completos que identifiquen claramente el campo, no uso prefijos para identificar el tipo ni en las variables, pero ya son gustos, ejemplos:

producto
codigo_barras
codigo_interno
nombre
apellido_paterno
apellido_materno
domicilio
no_exterior
no_interior
municipio
ciudad
metodo_pago
tipo_cambio
iva
iva_base
iva_tasa
iva_importe

saludos
Antonio Meza

Jorge Gonzalez

unread,
Jun 12, 2015, 12:40:46 PM6/12/15
to publicesvfoxpro@googlegroups com
Esta bueno. Es costumbre, lo uso desde que programaba con dbase y foxbase bajo DOS y esa fue la nomenclatura que usabamos en clase.
Gracias por el consejo, creo que identificar el tipo de campo es conveniente para identificar bien cuando se usen variables que tengan algo que ver con el campo algo asi como:
int_Prodid
c_Nombre
c_Codigo_barra
c_Nombre_factura
n_precio1
n_precio2
n_precio3
c_ubicacion
c_Nombre_imagen
n_lapso_dias_compra

o sin los "_"
cNombre
cCodigoBarra.... etc

Que será de la vida de Fernando Bozzo?
--

Antonio Meza

unread,
Jun 12, 2015, 12:41:10 PM6/12/15
to publice...@googlegroups.com, sop...@avdelsistemas.com
ID es muy usando para hacer referencia al campo Primary Key de una tabla, mas relacionado con campos autoincrementable, Ejemplo:

Tabla Factura
id folio fecha

Tabla Facturas_detalles
id idfactura cantidad

De esa forma identifico rápidamente que el campo facturas_detalles.idfactura es una clave foránea que proviene del campo Facturas.id

saludos
Antonio Meza

Víctor Hugo Espínola Domínguez

unread,
Jun 12, 2015, 12:42:03 PM6/12/15
to publice...@googlegroups.com
Hola Joge.

Por convención casi universal significa: Este dato es clave primaria, es abreviación de IDENTITY (Identidad, identificador), y es recomendable que el mismo nombre se use en las tablas relacionadas, por ejemplo en la tabla FacturasDetalle debe usarse el nombre IdProducto, no hay problema de ambigüedad porque usarás el alias para evitar confusiiones.

Saludos,
Víctor.
Lambaré - Paraguay.

Jorge Gonzalez

unread,
Jun 12, 2015, 12:44:30 PM6/12/15
to publicesvfoxpro@googlegroups com
Ah ya agarré, es que no lo vi como i mayúscula, lo estaba viendo como "L" minúscula =l, me confundí, es idProducto. Está bueno.
--

Antonio Meza

unread,
Jun 12, 2015, 12:44:34 PM6/12/15
to publice...@googlegroups.com, resets...@gmail.com
Gracias Daniel por la explicación era una duda que tenia!!! porque solo leía que CA devolvía un cursor actualizable y si este lo haces a mano sin usar CA, si te desconectas se pierde la relación y el TableUpdate marca error.

saludos
Antonio Meza

Víctor Hugo Espínola Domínguez

unread,
Jun 12, 2015, 12:47:31 PM6/12/15
to publice...@googlegroups.com
Hola Jorge

Entre los profesionales de xBase las opiniones están divididas, yo prefiero sin el sufijo para los campos y sí para las variables y propiedades de objetos. Entre los expertos de SQL la mayoría prefiere no usar esos prefijos.

En resumen: Questión de gustos ;-)

Saludos,
Víctor.
Lambaré - Paraguay.

Jorge Gonzalez

unread,
Jun 12, 2015, 12:49:32 PM6/12/15
to publicesvfoxpro@googlegroups com
Esta bien, supongo que es costumbre, hábito ninguno de mis colegas aquí me ha hecho ese tipo de observación, pero si es asi, pues lo haremos como los profesionales de xBase
Igualmente gracias.
--

Antonio Meza

unread,
Jun 12, 2015, 12:59:09 PM6/12/15
to publice...@googlegroups.com, vich...@gmail.com
En foxydb uso la función GETFLDSTATE que me permite saber lo que paso con un registro siempre y cuando el cursor devuelto por SQLEXEC le hayas aplicado Buffering.

Entonces imagino que XMLAdapter que dices que identifica los cambios en un cursor es como la función que llame COMMAND que hace lo mismo, identifica los cambios que hiciste en un cursor en uno o varios registros y arma el SQL correspondiente.

Desde mi humilde punto de vista, OleDb ya chupo faros es decir ya le dio su recorte Microsoft, usar a estas alturas un desarrollo nuevo con DBF es de pensarse 2 veces al menos, Para usar XML con VFP sus funciones no te permiten obtener correctamente los datos, y ADO vs ODBC pues ya es cuestión de gustos, 

Entonces en lo personal sigo viendo mas fuerte a SPT que a CA, en lo personal.

Y nuevamente gracias Victor por la información valiosa y detallada.

saludos
Antonio Meza

Víctor Hugo Espínola Domínguez

unread,
Jun 12, 2015, 1:24:52 PM6/12/15
to publice...@googlegroups.com
Hola Jorge

Tampoco es algo que deba ser considerado como mala práctica, mas bien en desuso por su mal aspecto, pero Genexus por ejemplo lo tiene como norma obligatoria, la versión 6 es la que conozco y no sé como está el tema ahora mismo, con el agravante de que el total de carateres para nombrar un campo era 10, ese fue el principal motivo por el dejé de usar Genexus.

Aún no mencionaste que significa "ntof".

Saludos,
Víctor.
Lambaré - Paraguay.

Jorge Gonzalez

unread,
Jun 12, 2015, 1:28:46 PM6/12/15
to publicesvfoxpro@googlegroups com
ntof= name to fact.
Realmente es un identificativo rebuscado.
Las impresoras fiscales tienen un limite de 60 caracteres para el nombre de los artículos, entonces uso un nombre completo para usarlo como referencia y al momento de facturar mando a la impresora lo contenido en ese campo, en el ejemplo anterior le coloqué nombre_factura.

Que es Genexus?
--

Antonio Meza

unread,
Jun 12, 2015, 1:40:28 PM6/12/15
to publice...@googlegroups.com, sop...@avdelsistemas.com
Hola Jorge gracias por los comentarios!!

En conclusión CA es super bueno, solo le hace falta una librería mas completa para hacer lo mas sencillo, como por ejemplo FoxyDb para SPT.

saludos
Antonio Meza

Jorge Gonzalez

unread,
Jun 12, 2015, 1:41:10 PM6/12/15
to publicesvfoxpro@googlegroups com
Tu eres el creador de FoxyDb?
--

Antonio Meza

unread,
Jun 12, 2015, 1:58:25 PM6/12/15
to publice...@googlegroups.com, vich...@gmail.com
Hola Victor, en mi caso también prefiero que los campos de la base de datos no tengan prefijo, se ven mas bonitas jajajaj

Por ejemplo la base de datos MYSQL que usa el propio servidor de mysql no tiene prefijos, en SQL Server tampoco, en firebird tampoco, entonces quiere decir que no son buena practica, si lo fueran los propios servidores lo usaran en sus tablas internas, Y por ejemplo WordPress que es tan conocido y tampoco los usa

Es mas la DBC que trae VFP de ejemplo northwind.dbc usa nombres de campos sin prefijo, Ahh y usa campos Primary Key Autoincrementables como identificador de registro jajajajaj

La otra DBC de ejemplo de VFP la TestData.Dbc como que el ejemplo es mas veterano no usa autoincrementables, pero igual no usan prefijos para identificar el tipo de campo.

saludos
Antonio Meza

Víctor Hugo Espínola Domínguez

unread,
Jun 12, 2015, 1:58:51 PM6/12/15
to publice...@googlegroups.com
Hola Antonio


>Entonces imagino que XMLAdapter que dices que identifica los cambios en un cursor es como la función que llame COMMAND que >hace lo mismo, identifica los cambios que hiciste en un cursor en uno o varios registros y arma el SQL correspondiente.
El siguiente código guarda en una variable xml solo los cambios en el cursor y lo envía  a la capa de negocios para que esta a su vez la pase a la capa de datos para actualizar la tabla.

Local loDiffGram As Xmladapter
Local loOrdenes  As "CapaNegocios" Of "CapaNegocios.prg"

loOrdenes = Newobject("CapaNegocios", "CapaNegocios.prg")

loDiffGram = Createobject("XMLAdapter")
m.loDiffGram.AddTableSchema(Thisform.oXOrders.Tables.Item(1).Alias)
loDiffGram.IsDiffGram = .T.
m.loDiffGram.ToXML("lcDiffGramXML", , .F., .T., .T.)

m.loOrdenes.ActualizaOrdenes(m.lcDiffGramXML, Clientes.CustomerId)

>Entonces en lo personal sigo viendo mas fuerte a SPT que a CA, en lo personal.
Repito: SPT y CA no se pueden comparar, son cosas distintas, es como comparar la salsa bechamel con la tarta de jamón y queso, así como la salsa es un ingrediente de la tarta, SPT es un "ingrediente" de CA, por ejemplo cuando se ejecuta el método CursorFill el CA pasa el contenido de la propiedad SelectCmd al SPT para que este traiga los datos al cursor CA.Alias.

Saludos,
Víctor.
Lambaré - Paraguay.

Antonio Meza

unread,
Jun 12, 2015, 2:08:46 PM6/12/15
to publice...@googlegroups.com, sop...@avdelsistemas.com
Hola Jorge, 

Así es!! primero fue DBVFP, y ahora FoxyDb, porque no mejor me ayudas a agregar PostgreSql tu que lo conoces a foxydb y contribuyes con una buena causa y en vez de usar CA usas FoxyDb jejeje

No te creas!!! jajaj, usa CA, pero si puedes ayudarme con PostgreSql seria fabuloso!!

saludos
Antonio Meza

Jorge Gonzalez

unread,
Jun 12, 2015, 2:09:56 PM6/12/15
to publicesvfoxpro@googlegroups com
con todo gusto Antonio.
Me lo bajo y dame una especie de manual
--

Antonio Meza

unread,
Jun 12, 2015, 2:14:42 PM6/12/15
to publice...@googlegroups.com, vich...@gmail.com
Hola Victor, creo ya te lo había preguntado, pero entonces solo reporta los cambios hechos, es decir si un registro tiene 10 campos y modificas uno solo envía uno o envía todos de nuevo? y si son 30 registros y eliminas uno, cuando van de regreso solo elimina uno, o elimina todos y inserta todos de nuevo?

Otra pregunta como le hacen para obtener los valores de los campos ID autoincrementables cuando usas diferente servidor de base de datos

Y la otra que me falta saber es como le hacen para aplicar bloqueos de registros igual cuando se usan diferentes servidores de base de datos?

Son dudas que tengo y como ustedes usan CA pues deben saber de ello, si es automático con propiedades o lo tienen que hacer manual?

saludos
Antonio Meza

Antonio Meza

unread,
Jun 12, 2015, 2:25:02 PM6/12/15
to publice...@googlegroups.com, sop...@avdelsistemas.com
Acabo de subir la version 2.0 que cambia un poco en cuanto al uso con la versión 1.23, pero mejor usa la 2.0, apenas estoy actualizando la documentación de la 2.0

Los primeros pasos que son conectar y desconectar y realizar consultas

El problema es que si vez el código fuente de FoxyDb donde veas la propiedad .postgreSql veras que no tiene nada en los DO CASE, porque falta ponerle esos códigos según este servidor, a demás del Driver y puerto en sus propiedades como los tienen los otros servidores.

Si observas entre Mysql y MariaDb son iguales, pero Firebird maneja otras formas de bloquear, obtener el ultimo ID, obtener la fecha del servidor, la versión del servidor, etc 

Mejor deja publico un cuestionario de todo lo que necesito para integrar PostgreSql y de una vez Sql Server para que los integre en la librería y puedan hacer las pruebas.

saludos
Antonio Meza

Víctor Hugo Espínola Domínguez

unread,
Jun 12, 2015, 3:21:04 PM6/12/15
to publice...@googlegroups.com
Hola Antonio


>Hola Victor, creo ya te lo había preguntado, pero entonces solo reporta los cambios hechos, es decir si un registro tiene 10 campos y >modificas uno solo envía uno o envía todos de nuevo? y si son 30 registros y eliminas uno, cuando van de regreso solo elimina uno, o >elimina todos y inserta todos de nuevo?
Por dada registro modificado se genera un par de datos, el actual y el anterior incluyendo todos los campos, con respecto a la cantidad de campos que modifica es algo interno del CA, no sé si actualiza solo los campos cambiados o no. CA envía al servidor solo las novedades a nivel de registros y el método del update se configura con la propiedad UpdateType.

>Otra pregunta como le hacen para obtener los valores de los campos ID autoincrementables cuando usas diferente servidor de base de >datos
Eso no lo hace CA, se debe averiguar mediante SPT.

>Y la otra que me falta saber es como le hacen para aplicar bloqueos de registros igual cuando se usan diferentes servidores de base de >datos?
Por default CA usa transacción, se puede especificar que no la use con la propiedad UseTransaccions.

Un ejemplo de como actúa XmlAdapter, nada que ver con CA, es un auxiliar así como SPT.

Local loXaDiffGram As Xmladapter
Local lcXmlDiffGram

Select  *                                  ;
    From (_Samples + "\Northwind\Orders")  ;
    Where CustomerId = "ALFKI"             ;
    Into Cursor curOrders Readwrite
CursorSetProp("Buffering", 5, "curOrders")

Browse   && Modifica a tu gusto algunos registros

loXaDiffGram = Createobject("XMLAdapter")
m.loXaDiffGram.AddTableSchema("curOrders")
loXaDiffGram.IsDiffGram = .T.
m.loXaDiffGram.ToXML("CambiosOrders", , .T., .T., .T.)

Modify File CambiosOrders.XML Noedit

Select  *                                  ;
    From (_Samples + "\Northwind\Orders")  ;
    Where CustomerId = "ALFKI"             ;
    Into Cursor curOrders Readwrite
CursorSetProp("Buffering", 5, "curOrders")

lcXmlDiffGram = Filetostr("CambiosOrders.xml")
m.loXaDiffGram.LoadXML(m.lcXmlDiffGram)
loXaDiffGram.Tables.Item(1).Alias = "curOrders"
m.loXaDiffGram.Tables.Item(1).ApplyDiffgram()

Browse

Saludos,
Víctor.
Lambaré - Paraguay.

Antonio Meza

unread,
Jun 12, 2015, 3:51:59 PM6/12/15
to publice...@googlegroups.com, vich...@gmail.com
Hola!!! probé el ejemplo me marco error en MSXML 4.0 Service Pack 2, lo baje e instale de

https://www.microsoft.com/es-mx/download/details.aspx?id=19662

Y ya ejecute de nuevo el ejemplo y funciono perfecto, 

Para saber si usando CA actualiza todos los valores, lo que puedes hacer es traerte los datos de un registro X, modificas un solo campo, pero antes de enviar la actualización al servidor, usa un administrador de la DB, modifica de ese mismo registro otro campo y luego actualizas al servidor con CA, ahí veras si cambia el campo que modificaste manual desde el administrador o solo remplazo el que modificaste.

Y para saber si actualiza todos los registros, igual modificas varios registros en el cursor y a uno no le haces ningún cambio, pero este registro sin cambios lo eliminas desde el administrador, y luego actualizas desde CA al servidor para ver si ingresa de nuevo el que se borro previamente.

saludos
Antonio Meza

Jorge Gonzalez

unread,
Jun 12, 2015, 3:56:54 PM6/12/15
to publicesvfoxpro@googlegroups com
>Otra pregunta como le hacen para obtener los valores de los campos ID autoincrementables cuando usas diferente servidor de base de >datos
Cuando se guarda el nuevo registro en el servidor de base de datos, este le asigna su nuevo valor al campo autoincrementable lo que hago es actualizar el CA de consulta con CursorFill y actualizo el cursor que alimenta el grid, es decir, en ese cursor elimino todos los registros y lo vuelvo a llenar, de esa manera cuado se vea el grid, se ve con su nuevo ID.
--

Víctor Hugo Espínola Domínguez

unread,
Jun 12, 2015, 4:05:30 PM6/12/15
to publice...@googlegroups.com
Hola Antonio

Haré la prueba en cuanto pueda, pero aclaro que es por simple curiosidad, pues no creo que en un proceso interactivo debamos preocuparnos por millonésimas de segundos de diferencia entre uno y otro método y mientras la IST se la pasa feisbuceando entre registro y registro ;-)

Saludos,
Víctor.
Lambaré - Paraguay.

Antonio Meza

unread,
Jun 12, 2015, 5:20:51 PM6/12/15
to publice...@googlegroups.com, vich...@gmail.com
Según la ayuda de SqlExec() se puede enviar varias instrucciones SQL en un solo envió, con Sql Server funciona según han comentado, solo que lo eh intentando con Mysql, MariaDb y Firebird y no lo he logrado, hay que mover algunas propiedades con SQLSETPROP pero la ayuda no es muy clara o mas bien no le entiendo.

Por lo tanto si una tabla tiene 10 registros y modificaste 5 se requieren de 5 SqlExec() para actualizar el cursor, lo que seria mejor en uno solo. Aun así no tarda nada, ya que ademas solo viajan los campos modificados por cada registro.

saludos
Antonio Meza

Antonio Meza

unread,
Jun 12, 2015, 5:22:55 PM6/12/15
to publice...@googlegroups.com, sop...@avdelsistemas.com
Pero entonces como haces para el caso de que es una Factura que obtienes el ID que vas a remplazar en la tabla detalles_factura.idfactura para hacer la relación?

saludos
Antonio Meza

Víctor Hugo Espínola Domínguez

unread,
Jun 12, 2015, 5:37:59 PM6/12/15
to publice...@googlegroups.com
Hola Antonio


>Por lo tanto si una tabla tiene 10 registros y modificaste 5 se requieren de 5 SqlExec() para actualizar el cursor, lo que seria mejor en >uno solo. Aun así no tarda nada, ya que ademas solo viajan los campos modificados por cada registro.
No sé y no se me ocure como averiguar el tipo de update que arma el CA para actualizar solo los registros modificados, pero no creo que envíe varios updates pudiendo enviar solo uno del tipo:
       Update Tabla Set campo1 = valor1, ... campoN = valorN Where IdTabla IN (Id1, ...IdN) o algo similar.
Ahora si va a actualizar solo los campos modificados debe armar x updates, uno por cada registro y luego enviarlos al servidor, pero repito no sé como lo hace. Lo que sí sé que hace es que actualiza solo las novedades(Insert, Update, Delete).

Saludos,
Víctor.
Lambaré - Paraguay.

Antonio Meza

unread,
Jun 12, 2015, 6:34:33 PM6/12/15
to publice...@googlegroups.com, vich...@gmail.com
Si la función .Command() de foxydb se encarga de armar el SQL necesario, si por ejemplo insertaste, eliminaste y actualizaste registros pues se genera el SQL correspondiente, lo que necesito es poder enviarlo separado por ";" pero no logro entender la ayuda para hacer eso con SqlExec() voy a preguntar en el foro en un tema nuevo.

saludos!!


El viernes, 12 de junio de 2015, 16:37:59 (UTC-5), Víctor Hugo escribió:
Hola Antonio

Víctor Hugo Espínola Domínguez

unread,
Jun 12, 2015, 6:37:28 PM6/12/15
to publice...@googlegroups.com
Hola Antonio

Con SqlServer eso funciona OK, pero con otros motores NO :-(

Saludos,
Víctor.
Lambaré - Paraguay.

Víctor Hugo Espínola Domínguez

unread,
Jun 12, 2015, 7:01:56 PM6/12/15
to publice...@googlegroups.com
Hola Jorge

Le eché un vistazo al código que enviaste y me doy cuenta de que no estás captando la idea central de la programación en 3 capas:

Capa Interfase de Usuario          Datos            Capa Reglas de Negocios           Datos            Capa Datos
             UI                       <------------------------>             BO                      <------------------------>       DO

UI: User Interfase
BO: Business Object
DO: Data Object

Un ejemplo de uso: El formulario necesita el producto cuyo código de barra está en el txtCodBarra.Value
lcCodBarra = ThisForm.txtCodBarra.Value
llOK = ThisForm.TraerProductoPorCodBarra(m.lcCodBarra)
If m.llOK
     * Procesar el producto
...
...
*-- Método TraerProductoPorCodBarra
Lparameters tcCodBarra
Local loProducto

loProducto = Newobject("boProducto", "boProducto.prg")

??????  = m.loProducto.TraerProductoPorCodBarra(m.tcCodBarra)

Bueno hasta aquí llegamos, tenemos que decidir como UI recibirá el resultado desde BO. Las alternativas son Objeto, Colección, Array, Xml, Cursor ??
También se debe decidir como avisa BO a UI que ocurrió un error.

If Ocurrió un Error
     ThisForm.ProcesarError(lcRetorno)
Endif

Espero tu respuesta con respecto a estas cuestiones y la nomenclatura de tus campos.

Saludos,
Víctor.
Lambaré - Paraguay.

Jorge Gonzalez

unread,
Jun 15, 2015, 8:00:40 AM6/15/15
to publice...@googlegroups.com, vich...@gmail.com
Hola Victor.
Hasta los momentos solo he hecho la selección de registros para mostrarlo en el grid, ahora viene el paso de la selección por parte del usuario del producto a consultar, asi como el paso de inserción de un nuevo registro y el de edición.
Una vez que lo haga te lo muestro para verificar si capté bien el trabajo a tres capas.

Saludos 
 

Jorge Gonzalez

unread,
Jun 15, 2015, 8:01:23 AM6/15/15
to publice...@googlegroups.com, sop...@avdelsistemas.com
Quien coloca como finalizado el tema?
No recuerdo haberlo hecho yo

Víctor Hugo Espínola Domínguez

unread,
Jun 15, 2015, 12:48:20 PM6/15/15
to publice...@googlegroups.com
Hola Jorge

No te conviene seguir avanzando porque el problema lo tienes en el concepto y definición de la capa da datos, hay varios estilos de implementación de la capa de datos uno de ellos es el siguiente:

- Una o más clase(s) de acceso a datos genérica(s), contiene(n) propiedades y métodos usados en todos los sistemas y se encarga(n) de la conexión/desconexión al/del servidor.

- Una clase por cada objeto de negocio, por ejemplo: do_Producto, do_Factura, do_Cliente

- En vez de lo anterior si el proyecto es pequeño se puede tener una clase por cada módulo del sistema: VentasDatos, ContabDatos, StockDatos, etc...

Esta(s) clase(s) de la capa de datos se pueden probar desde la ventana de comandos, una vez probadas se confecciona la capa de negocios que también se prueba en la ventana de comandos y por último se hacen los formularios.

Saludos,
Víctor.
Lambaré - Paraguay.

Jorge Gonzalez

unread,
Jun 15, 2015, 4:01:59 PM6/15/15
to publicesvfoxpro@googlegroups com
Hola Victor.
Mi conclusión es usar clases por módulo, es decir, para el módulo de facturación hacer una clase de datos, una clase de negocio.
Para el módulo de productos una clase de datos, una clase de negocio.}
Ahora, este sistema pretende ser grande.
La primera fase es facturación y lo que conlleva para eso, manejo de productos, clientes, inventario.
Luego cuentas por pagar, etc.
Eso es lo que he hecho hasta los momentos. Ahora. quise separar tu clase separada de las que yo hago, la tuya la usaría estrictamente para crear los cursor adapter y esta clase la usarían todas las interfaces de usuario, y asi mantengo la modalidad de tener clases de datos y negocios para cada módulo. Ahora, si esto se escapa del modelo de programación en capas, no tengo problemas en unificarlas si eso me trae ventajas, profesionalidad, etc.

Saludos
Jorge
--

Jorge Gonzalez

unread,
Jun 15, 2015, 4:04:30 PM6/15/15
to publicesvfoxpro@googlegroups com
Esta semana estaré trabajando hasta tarde en la noche en este proyecto para adelantarlo lo mas posible. Asi que sus ayudas son valiosas.
Gracias a todos
--

Víctor Hugo Espínola Domínguez

unread,
Jun 15, 2015, 8:30:22 PM6/15/15
to publice...@googlegroups.com
Hola Jorge

OK, ya decidiste el estilo en que harás la capa de datos, ahora falta que elijas el formato de los datos que se enviarán entre las capas. Las opciones son: Xml, Objetos, Arrays, Cursores, Colecciones.

Saludos,
Víctor.
Lambaré - Paraguay.

Antonio Meza

unread,
Jun 15, 2015, 11:13:51 PM6/15/15
to publice...@googlegroups.com, vich...@gmail.com
Hola!! en mi caso usaria Cursores!!

Los Cursores son una maravilla, programar en VFP sin usar cursores es como andar en una moto sin gasolina jajajaj

saludos
Antonio Meza

Víctor Hugo Espínola Domínguez

unread,
Jun 16, 2015, 12:22:07 AM6/16/15
to publice...@googlegroups.com
Hola Antonio

Es cierto, el cursor es lo máximo para manipular datos relacionales, pero tiene el inconveniente de que no puede ser pasado como parámetro. El truco sería pasar el nombre del cursor como parámetro para "compartir" el cursor entre las dos capas y quizás sea necesrio en algunos casos pasar también el DataSessionId. Pero esto es un truco porque no estamos comunicando datos entre capas sino que estamos compartiendo datos entre capas, y eso se llama "acople", que en cualquier momento puede pasarnos la factura.

Otra desventaja es que al compartir cursores entre capas no podemos generar servidores COM DLL, esto me es difícil de explicar así que les remito a: https://www.universalthread.com/ViewPageArticle.aspx?ID=702, vale la pena leer y probar este material.

Saludos,
Víctor.
Lambaré - Paraguay.

Antonio Meza

unread,
Jun 16, 2015, 2:12:35 AM6/16/15
to publice...@googlegroups.com, vich...@gmail.com
No le veo ninguna necesidad en crear un COM o DLL si estas desarrollando en VFP y el tiene todo lo necesario para manipular los datos, en que casos crees que es necesario? hablando de datos!! si voy a compartir con otra aplicación usaria XML, JSON, y en el peor de los casos XLS, jeje

Es muy cierto no puedes pasar como un parámetro un cursor!!

Ahora bien, creo que el problema se resuelve entendiendo el alcance que va a tener el cursor devuelto por la clase, el modelo o capa, como quieran llamarle.

No se a que te refieres con acoplar o compartir datos entre las capas, la capa de negocios le solicita a la capa de datos la información, entonces la capa de datos devuelve en mi caso un Cursor, el cual no tengo que compartir ni pasar, si no solamente utilizar porque ya lo tengo en memoria, si fuera un objeto entonces si tengo que andarlo pasando posiblemente entre capas para generarlo, pero esa es la ventaja que tiene VFP al usar cursores, y por ejemplo en que momento podrías tener problemas como comentas que te puede pasar factura? recordar que estamos desarrollando en VFP.

Ejemplo: El usuario solicita el registro ID = 10 de la tabla Personas, este se encuentra en un servidor MariaDb donde esta la base de datos, entonces lo que hago es utilizar mi capa de acceso a datos que esta contenida en un PRG para que me devuelva el registro que esta solicitando el usuario.

En mi caso uso FoxyDb como una capa intermedia entre VFP y el servidor de datos, lo que me permite poder cambiar entre MariaDb, Mysql y FireBird con solo cambiar una propiedad de la librería a diferencias de usar SPT directamente sobre un solo servidor de datos como hacen algunos, a demás de un código mas simple en mi capa de datos, ejemplo

* Defino mi clase de acceso al Servidor de datos, en este caso voy a usar MariaDb
thisform.oDb = NewObject("FoxyDb","data\FoxyDb.prg")
thisform.oDb.engine = thisform.oDb.MariaDb
* Defino propiedades de Conexión y dejo listo para cuando quiera conectarme
* etc
..
..
* Abro el formulario con sesión privada de datos donde el usuario determino la Persona ID = 10
* Creo mi Objeto de datos para obtener de la tabla Personas
thisform.oPersonas = NewObject("personas","data\personas.prg")
* Me traigo el registro solicitado de mi objeto de datos
thisform.oPersonas.Persona(thisform.oDb,10)
* Y obtengo el cursor con el registro solicitado
Select Personas

Si tengo la opción de editar el registro entonces una vez que el usuario presiono el botón guardar solo tengo que decirle a mi clase de datos o capa de datos que guarde.
thisform.oPersonas.Guardar(thisform.oDb,"persona")


Si mis datos devueltos fueran objetos entonces mi Cursor Personas seria el objeto y los campos serian las propiedades, al final del día es lo mismo, pero en VFP un cursor tiene mucho mas ventajas que crear un objeto a partir de los datos, para empezar siempre vas a obtener un cursor que tienes que transformar en un objeto, ahí ya es realizar un proceso mas, luego identificar los cambios que haces en un objeto o propiedad de un objeto es mas complicado, que usar las funciones nativas de VFP que tiene para la manipulación de un cursor.

Ejemplo resumido de la clase datos Personas

Define clase Personas as custom
     * Propiedades
        ...
        fields = "id, nombre, paterno, materno, ....."
        ...

      FUNCTION Persona
          lparameter __oDb as Object, __id as Integer, __cursorName as Character
          local __commandSql, ... ... ...
          * Validar Parámetros
            ...
            ....
          * Consulta
            __commandSql = "Select " + this.fields + " from personas where id = ?__id"
            if __oDb.Query(__commandSql,__cursorName)
                return .t.
           else
                return .f.
           endif
      ENDFUNC
  
      FUNCTION Personas_listado
        ....
      ENDFUNC

      FUNCTION Guardar()
        lparameter __oDb as Object, __cursorName as Character
        * Verificar si hay Cambios
           if not __oDb.CursorChange(__cursorName)
                 return .f.
           endif
        * Actualizar
           if not __oDb.Update(__cursorName)
                 return .f.
           endif
        * Aplicar
          if __oDb.Commit()
                return .t.
          else
                return .f.
         endif
      ENDFUNC

ENDDEFINE


En mi caso por cada tabla tengo una clase datos, o por cada modulo según sea el caso, a demás aprovecho las ventajas de la Programación orientada a objetos (POO) y tengo una clase genérica donde tengo la función Guardar() que heredo a la clase Personas, imaginen repetir código en cada clase seria de locos, así que hago el mejor uso posible de la POO.

saludos
Antonio Meza

Jorge Gonzalez

unread,
Jun 16, 2015, 2:06:49 PM6/16/15
to publice...@googlegroups.com

Objetos, cursores de consulta y CA, no entiendo el concepto de xml, y no manejo muy bien los array

Jorge Gonzalez

unread,
Jun 16, 2015, 2:06:51 PM6/16/15
to publice...@googlegroups.com

Tal cual lo entiende Antonio lo entiendo yo. Aunque el pasar data como parámetro me gustaría saberlo al igual que la filosofía del XML

Víctor Hugo Espínola Domínguez

unread,
Jun 16, 2015, 3:04:25 PM6/16/15
to publice...@googlegroups.com
OK, pero no entiendo el proceso "Productos", porque "Producto" es más un objeto que un proceso. Aclara a qué proceso te refieres con "Productos".

Pasa la estructura definitiva de la tabla Productos para confeccionar la primera versión de la capa de datos.

Saludos,
Víctor.
Lambaré - Paraguay.

Fernando D. Bozzo

unread,
Jun 16, 2015, 3:07:35 PM6/16/15
to publice...@googlegroups.com, maov...@gmail.com, sop...@avdelsistemas.com
Hola José Mario, por favor, quita la marca de finalizado, que este hilo no lo comenzaste tu y sigue abierto.

Gracias.-



El viernes, 12 de junio de 2015, 15:16:34 (UTC+2), Jose Mario escribió:

Víctor Hugo Espínola Domínguez

unread,
Jun 16, 2015, 3:23:00 PM6/16/15
to publice...@googlegroups.com
Hola Antonio

>No le veo ninguna necesidad en crear un COM o DLL si estas desarrollando en VFP y el tiene todo lo necesario para manipular los >datos, en que casos crees que es necesario? hablando de datos!! si voy a compartir con otra aplicación usaria XML, JSON, y en el peor >de los casos XLS, jeje
>
>Es muy cierto no puedes pasar como un parámetro un cursor!!
>
>Ahora bien, creo que el problema se resuelve entendiendo el alcance que va a tener el cursor devuelto por la clase, el modelo o capa, >como quieran llamarle.

En un mensaje anterior ya había mencionado que el tipo de datos a ser usado para la comunicación entre capas depende de algunas características de la aplicación.
- Si la aplicación será monolítica, todo en un .exe  o .app entonces puedes usar cualquier tipo de datos.
- Si la aplicación será distribuida, UI en una pc, BO y DO en otra como servidor COM DLL, entonces el cursor está descartado.
- Si la BO y/o DO serán usadas por otras aplicaciones, tampoco puedes usar cursor.

Con respecto a qué necesidad hay de usar esas técnicas NPI pues yo no las he usado aún.

>No se a que te refieres con acoplar o compartir datos entre las capas, la capa de negocios le solicita a la capa de datos la información, >entonces la capa de datos devuelve en mi caso un Cursor, el cual no tengo que compartir ni pasar, si no solamente utilizar porque ya lo >tengo en memoria, si fuera un objeto entonces si tengo que andarlo pasando posiblemente entre capas para generarlo, pero esa es la >ventaja que tiene VFP al usar cursores, y por ejemplo en que momento podrías tener problemas como comentas que te puede pasar >factura? recordar que estamos desarrollando en VFP.

Acople entre módulos/capas es compartir datos globales entre ellos, tanto en la programación estructurada como en la POO y la programación en capas se recomienda que el acople sea nulo o mínimo..

Una factura puede ser que alguien te solicite que Foxydb pueda proveer datos un documento de Word o Excel, o una UI hecha en Lazarus.

Saludos,
Víctor.
Lambaré - Paraguay.

Mario Oviedo

unread,
Jun 16, 2015, 4:14:04 PM6/16/15
to publice...@googlegroups.com
no yo no he hecho nada, y si lo hice no se como, perdonenme

como hago eso

Mario Oviedo

unread,
Jun 16, 2015, 4:15:45 PM6/16/15
to publice...@googlegroups.com
solo soy lector, porque me gustan sus temas, pero yo no he finalizado nada

Antonio Meza

unread,
Jun 16, 2015, 6:05:21 PM6/16/15
to publice...@googlegroups.com, maov...@gmail.com
Hola Jose Mario, posiblemene sin querer presionaste la opcion que dice MARCAR COMO FINAIZADO, solo dale click nuevamente para que se desmarque.

saludos
Antonio Meza

Antonio Meza

unread,
Jun 16, 2015, 7:11:43 PM6/16/15
to publice...@googlegroups.com, vich...@gmail.com
Hola Victor!!

Desde mi punto de vista son 2 cosas distintas, una capa para el acceso a datos desde la Aplicación de VFP y otra capa para exportar o compartir datos entre aplicaciones o lenguajes.

Para el primer caso el acceso a datos desde la propia aplicación de VFP no le veo ninguna utilidad crear un DLL o COM si dispongo de CursorAdapter, Vistas Remotas y SPT (FoxyDb), que creo es en lo que estas confundiendo un poco a Jorge.

Y para poder exportar o incluso importar datos entre aplicaciones diferentes (otros lenguajes) se desarrolla otra capa que puede ser una DLL perfectamente, tal el caso de que VFP no tiene la capacidad de consumir algunos  WebServices como han comentado en el foro, entonces puedes usar C# para proveer esa DLL que se conectara y te devolverá un XML o JSON, que ya podrás manipular en VFP incluso convertirlo en Cursor y enviarlo a tu capa de datos interna.

Otro ejemplo en el caso de Factura Electrónica en México, en mi caso compre una DLL en Delphi, a la que le paso un archivo .INI el cual la librería convierte a XML y envía a un webservice el cual me devuelve un XML que uso desde VFP y envió los datos necesarios por mi capa de datos (FoxyDb).al servidor de base de datos.

Por lo tanto uso la librería FoxyDb (SPT)  para comunicar mi aplicación con el servidor de base de datos y uso otra librería para comunicarme con la DLL de Delphi, por ello es que en lo personal para mi son 2 cosas muy distintas, y no voy a limitar mi capa de datos interna para satisfacer una necesidad externa, no se si me explico?

Imagina en el peor de los casos crear un DLL en VFP para accesar a Mysql, y luego este usarlo en C# o Lazarus seria bueno, pero el propio C# y Lazarus disponen de librerías para el acceso a Mysql, que ventaja vas a tener? no se si me explico? ahora si compilas para Linux tu DLL en VFP no servirá de nada, son ejemplos nada mas.

saludos
Antonio Meza


El martes, 16 de junio de 2015, 14:23:00 (UTC-5), Víctor Hugo escribió:
Hola Antonio

>No le veo ninguna necesidad en crear un COM o DLL si estas desarrollando en VFP y el tiene todo lo necesario para manipular los >datos, en que casos crees que es necesario? hablando de datos!! si voy a compartir con otra aplicación usaria XML, JSON, y en el peor >de los casos XLS, jeje
>
>Es muy cierto no puedes pasar como un parámetro un cursor!!
>
>Ahora bien, creo que el problema se resuelve entendiendo el alcance que va a tener el cursor devuelto por la clase, el modelo o capa, >como quieran llamarle.

En un mensaje anterior ya había mencionado que el tipo de datos a ser usado para la comunicación entre capas depende de algunas características de la aplicación.
- Si la aplicación será monolítica, todo en un .exe  o .app entonces puedes usar cualquier tipo de datos.
- Si la aplicación será distribuida, UI en una pc, BO y DO en otra como servidor COM DLL, entonces el cursor está descartado.
- Si la BO y/o DO serán usadas por otras aplicaciones, tampoco puedes usar cursor.

Con respecto a qué necesidad hay de usar esas técnicas NPI pues yo no las he usado aún.

>No se a que te refieres con acoplar o compartir datos entre las capas, la capa de negocios le solicita a la capa de datos la información, >entonces la capa de datos devuelve en mi caso un Cursor, el cual no tengo que compartir ni pasar, si no solamente utilizar porque ya lo >tengo en memoria, si fuera un objeto entonces si tengo que andarlo pasando posiblemente entre capas para generarlo, pero esa es la >ventaja que tiene VFP al usar cursores, y por ejemplo en que momento podrías tener problemas como comentas que te puede pasar >factura? recordar que estamos desarrollando en VFP.

Acople entre módulos/capas es compartir datos globales entre ellos, tanto en la programación estructurada como en la POO y la programación en capas se recomienda que el acople sea nulo o mínimo..

Una factura puede ser que alguien te solicite que Foxydb pueda proveer datos un documento de Word o Excel, o una UI hecha en Lazarus.

Saludos,
Víctor.
Lambaré - Paraguay.

Mario Oviedo

unread,
Jun 16, 2015, 8:52:09 PM6/16/15
to publice...@googlegroups.com

Perdonenme si la regue

Mario Oviedo

unread,
Jun 16, 2015, 8:53:06 PM6/16/15
to publice...@googlegroups.com

Offff topic Va ganando argentina a uruguay

Víctor Hugo Espínola Domínguez

unread,
Jun 16, 2015, 10:40:03 PM6/16/15
to publice...@googlegroups.com
Hola Jorge

Adjunto la clase de capa de datos inicial, en el sentido que iremos agregando propiedades y métodos según sea necesario, es importante que entiendas la filosofía y el funcionamiento para seguir refinando y ampliando la(s) capa(s).

Como probar:
- Cambia el string de conexión en el bloque text/endtext del evento INIT.
- En la ventana de comandos:
od = NEWOBJECT("kpadatos", "kpadatos.prg")
r = od.TraerRegPorClave("Productos","IdProducto", "1")
? r
BROWSE
r = od.TraerRegPorFiltro("Productos","Upper(Descripcion) Like 'LE%'")
? r
BROWSE
r = od.TraerRegPorFiltro("FacturasDet fd, Productos pr","fd.IdProducto = pr.IdProducto")
? r
r = od.TraerRegPorFiltro("Paises pai, Provincias pvn","pai.IdPais = pvn.IdPais")
? r
BROWSE
r = od.TraerRegPorFiltro("Paises pai, Provincias pvn","pai.IdPais = pvn.IdPais And pai.IdPais = 1")
? r

*  sustituye los nombres de tablas y campos por los que tienes en tu base de datos

Saludos,
Víctor.
Lambaré - Paraguay.

kpadatos.PRG

Carlos Miguel FARIAS

unread,
Jun 17, 2015, 9:35:37 AM6/17/15
to Grupo Fox
Argentina le gano a Uruguay. Es evidente que nuestro alimento balanceado para canes es mejor

Víctor Hugo Espínola Domínguez

unread,
Jun 18, 2015, 6:30:29 PM6/18/15
to publice...@googlegroups.com
Hola Jorge

Adjunto la clase de capa de datos, se agregan los métodos para grabar datos.

Saludos,
Víctor.
Lambaré - Paraguay.

kpadatos.PRG
vhecageneric.prg

Jorge Gonzalez

unread,
Jun 22, 2015, 12:20:51 PM6/22/15
to publicesvfoxpro@googlegroups com
Gracias Mr. Hugo.
Con respecto a la capa de datos vhecageneric.prg
Porque están estas líneas? están para prueba?

loMyCustomer = Createobject("vhecageneric", m.luConexion) && Dbf: nombre de la base de datos | nHandle de Sql
With m.loMyCustomer
*  .SelectCmd = 'select * from Northwind..Customers'
.Alias   = "crsCustomers"
* .Alias   = "crsProductos"
.SelectCmd = "select * from customers"
* .SelectCmd = "select * from Productos"
.Nodata   = .F.
.QueryFill()
.MakeUpdatable("Customers", "customerID", .T.) && enable updating
* .MakeUpdatable("Clientes", "IdCliente", .F.) && enable updating
* .MakeUpdatable("Productos", "IdProducto", .F.) && enable updating
*  .MakeUpdatable('Northwind..Customers','customerID',.F.) && enable updating
*  .MakeUpdatable('Customer','cust_no',.F.) && enable updating
Messagebox(.UpdatableFieldList + Chr(13) + .UpdateNameList)
Browse
Tableupdate(1, .T., .Alias)
Endwith

porque después es que viene el Define Class...
eso me tiene confundido
--

It is loading more messages.
0 new messages