25 de julio de 2017

Registrando cambios a vistas locales - Actualización

Artículo original: Logging Changes to Local Views - Updated
Autor: Nancy Folsom
Traducido por: Luis María Guayán


Escribí un artículo en el boletín de noticias de VFUG sobre una sencilla manera de rastrear los cambios a las vistas y a las tablas usando eventos de la base de datos. Este artículo muestra los retoques que he hecho durante los dos últimos dos meses, que incluye un sencillo informe de cambios.

Nota del traductor: El artículo que indica Nancy se lo puede ver en: Registrando cambios a vistas locales

¿Eventos de la base de datos con código en un archivo PRG  externo o en procedimientos almacenados?

Originalmente,  he planeado utilizar un archivo PRG externo para los eventos de la base de datos en vez de los procedimientos almacenados del contenedor de la base de datos. Mi razón para esto era porque utilizo múltiples contenedores de base de datos en mis proyectos y no quise cortar y pegar código, ya que es tan difícil mantener los cambios sincronizados. Esta opción funcionó bien durante el desarrollo. Sin embargo, esto causó problemas cuando actualicé las bases de datos de un cliente, que son usadas para abastecer una aplicación ASP.NET. Confieso que yo no tuve el tiempo ni los recursos para comprender completamente el problema. Sin embargo, en resumen, la aplicación en ASP.NET devolvió errores de OLEDB. Entiendo la razón por la cual un evento de la base de datos en un archivo PRG externo causaría el problema para una interfase Web; sin embargo no he tenido tiempo para investigar a fondo porqué el desencadenante dbc_AfterModifyView se dispararía.

Otro asunto surgió conjuntamente con usar la herramienta Stonefield Data Toolkit (SDT) (versión 6.1c). Esta herramienta permite que utilice los eventos de una base de datos en archivos PRG externos, así que esto no era el problema, y creará el código SDT para los eventos de la base de datos. SDT utiliza los procedimientos almacenados (SPs), sin embargo, no utilizará el archivo PRG externo que he seleccionado. Los eventos de la base de datos basados en archivos PRG tienen prioridad sobre los procedimientos almacenados. Eso no es un problema insuperable, puesto que es fácil mover el código de SDT desde el SPs. Considerando el primer problema, sin embargo, he decidido utilizar el SPs. Por motivos simples, continuaré utilizando un PRG externo para este artículo.

Nueva versión del programa para crear los eventos de base de datos y la tabla de registro

El siguiente código es una versión revisada del código de instalación del último artículo. Esto crea una base de datos de registro de modificaciones y la tabla. Añadí un pequeño campo de comentario para mostrar en el informes de modificaciones, y agregué valores por defecto para tTimeStamp y cComment. El campo cComment pretende cumplir el objetivo de contener los comentarios en el código ya que no podemos comentar las vistas locales. Además está actualizado.

* ModifyViewTracker_Part_II.PRG
*
* 2005.05.20 Nancy Folsom, Pixel Dust Industries
* Este programa crea un contenedor de base de datos y una tabla
* para registrar las modificaciones en vistas y tablas
*
* * Se asume que se ejecuta en el directorio de desarrollo
* * ¡Cuidado! Como cualquier código que cambia datos, primero 
* * haga un respaldo de sus datos, y pruebe el código sobre algunos
* * datos de prueba antes de ejecutarlo sobre datos de producción
*
* Creamos el registro de modificaciones
*
Close Databases All
If File(Fullpath('ChangeLog.DBC'))
  Delete File (Fullpath('ChangeLog.DBC'))) recycle
Endif
Create Database ChangeLog
  Set Database To ChangeLog
If File(Fullpath('ChangeLogEvents.DBF'))
  Delete File (Fullpath('ChangeLogEvents.DBF'))) recycle
Endif
Create Table ;
  ChangeLogEvents (;
  IID I Autoinc, ;
  cObjectName C(254), ;
  cAction C(254) Default Program(), ;
  mValue M, ;
  cComment C(254) Default Iif(Version(2)=2,;
  InputBox("Comment:","Comment Change",""),""),    ;
  tTimeStamp T Default Datetime())

Local lcDBC, lcLogFile, lcPRG
*
* Creamos los eventos PRG usando los comandos TEXT y StrToFile()
*
TEXT to lcPrg textmerge noshow

Procedure dbc_AfterCreateView(cViewName,lRemote)
  * Almacenamos la estructura de la sentencia SQL
  Local lnSelect
    lnSelect = Select()
  * Almacenamos la definición SQL
  Insert Into ChangeLog!ChangeLogEvents ( ;
    mValue,cObjectName ) Values ( ;
    DBGetProp(cViewName,"VIEW","SQL"),cViewName)
  Use In ChangeLogEvents
    Select (lnSelect)
Endproc
Procedure dbc_AfterModifyView(cViewName,lChanged)
  * Almacenamos la estructura de la sentencia SQL
  If lChanged
    Local lnSelect
    lnSelect = Select()
    * Almacenamos la definición SQL
    Insert Into ChangeLog!ChangeLogEvents ( ;
      mValue,cObjectName ) Values ( ;
      DBGetProp(cViewName,"VIEW","SQL"),cViewName)
    Use In ChangeLogEvents
    Select (lnSelect)
  Endif
Endproc
Procedure dbc_AfterCreateTable(cTableName,cLongTableName)
  * Almacenamos la estructura y la información acerca de los índices de la tabla
  Local lcAlias
    lcAlias = JustStem(cTableName)
  Insert Into ChangeLog!ChangeLogEvents (;
    mValue,cObjectName) Values (;
    GetStructure(lcAlias),Justfname(cTableName))
  Use In ChangeLogEvents
  Select (lcAlias)
Endproc

Procedure dbc_AfterModifyTable(cTableName,lChanged)
  * Almacenamos la estructura y la información acerca de los índices de la tabla
  If lChanged
    Local lcAlias
      lcAlias = JustStem(cTableName)
    Insert Into ChangeLog!ChangeLogEvents (;
      mValue,cObjectName) Values (;
      GetStructure(lcAlias),cTableName)
    Use In ChangeLogEvents
    Select (lcAlias)
  Endif
Endproc
Procedure GetStructure(tcAlias)
  Local lni,lnj,laArray[1],lcStructure
    lcStructure = "Field structures"+Chr(13)
  Select (tcAlias)
  * Por cada campo en la tabla ...
  For lni = 1 To Afields(laArray)
    For lnj = 1 To Alen(laArray,2) - 1
      * ... capturamos la estructura de la tabla
      lcStructure = lcStructure+;
      Transform(laArray[lni,lnj])+","
    Next lnj
    lcStructure = lcStructure+;
    Transform(laArray[lni,lnj])+Chr(13)
  Next lni
  lcStructure = lcStructure+Chr(13)+"Indices"+Chr(13)
  * Por cada etiqueta de índice ...
  For lni=1 To Ataginfo(laArray)
    For lnj=1 To Alen(laArray,2) - 1
      * ... capturamos el nombre de la etiqueta y la expresión
      lcStructure=lcStructure+laArray[lni,lnj]+","
    Next lnj
    lcStructure=lcStructure+laArray[lni,lnj]+Chr(13)
  Next lni
  Return lcStructure
Endproc
ENDTEXT
* Guardamos la cadena que hicimos con TEXT en un PRG.
lcLogFile="DataBaseEvents.PRG"
If File(Fullpath("DataBaseEvents.PRG"))
  lcLogFile=Forceext(Putfile("Save PRG As","DataBaseEvents","PRG"),;
    'PRG')
Endif
Strtofile(lcPRG,lcLogFile)
Compile (lcLogFile) 

La siguiente porción de código puede ser copiada a un PRG y ejecutada como una prueba. Este código creará una base de datos, una tabla y una vista de ejemplo. Esto también modificará la tabla. Cuando la tabla y la vista son creadas, y cuando la estructura de tabla es modificada, las entradas de registro serán hechas. Ingreso por teclado un cComment. Generalmente cada vez que un registro es insertado en la tabla de registro de modificaciones, le será solicitado un breve comentario a raíz del valor por omisión.

* DemoChangeLog.PRG
*
Close Databases All
Set Exclusive On
If File(Fullpath('ChangeLogExample.DBC'))
  Delete File Fullpath('ChangeLogExample.DBC') recycle
Endif
Create Database ChangeLogExample
Set Database To ChangeLogExample
* Habilitamos los eventos de la base de datos y apuntamos al 
* archivo PRG creado en el PRG de instalación
DBSetProp("ChangeLogExample","Database","DBCEvents",.T.)
DBSetProp("ChangeLogExample","Database","DBCEventFilename",;
  lcLogFile)
If File(Fullpath('ChangeLogExample.DBF'))
  Delete File Fullpath('ChangeLogExample.DBF') recycle
Endif
* Creamos la tabla: Ingresamos un comentario por teclado
Keyboard "Created table 'ChangeLogExample'" + Chr(13)
Create Table ChangeLogExample (;
  IID I Autoinc Primary Key, ;
  tTimeStamp T Default Datetime())
Close Tables All
* Creamos la vista: Ingresamos un comentario por teclado
Keyboard "Created view 'cDescription'" + Chr(13)
Create Sql View lv_ChangeLogExample As ;
  select * From ChangeLogExample
* Modificamos la tabla
Keyboard "Added field 'cDescription'" + Chr(13)
Alter Table ChangeLogExample ;
  Add Column cDescription C(32)
Close Databases All
Public oform1
oform1 = Newobject("form1")
oform1.Show
Return

Finalmente tendré ganas de ver la diferencia exacta entre dos versiones de, en mi caso, una vista. En el camino de ese objetivo, primero hice un formulario sencillo que lista los cambios, dentro de un rango de fechas, y si se quiere, un detalle del cambio. El siguiente código puede ser ejecutado en un directorio con la tabla ChangeLogEvents. Esto debería parecerse a lo que se muestra en la siguiente figura:

Aquí está el código del formulario.

**************************************************
*-- Form:         form1 (changelog_report.scx)
*-- ParentClass:  form
*-- BaseClass:    form
*-- Time Stamp:   05/20/05 06:11:04 PM
*
Define Class form1 As Form
  Height = 480
  Width = 640
  Caption = "Form1"
  startdate = {}
  enddate = {}
  Name = "Form1"
  Add Object line1 As Line With ;
    Height = 0, ;
    Left = 2, ;
    Top = 37, ;
    Width = 636, ;
    Name = "Line1"
  Add Object line2 As Line With ;
    Height = 0, ;
    Left = 2, ;
    Top = 158, ;
    Width = 636, ;
    Name = "Line2"
  Add Object text1 As TextBox With ;
    Century = 0, ;
    ControlSource = "thisform.StartDate", ;
    Format = "D", ;
    Height = 23, ;
    Left = 70, ;
    Top = 2, ;
    Name = "Text1"
  Add Object text2 As TextBox With ;
    ControlSource = "Thisform.EndDate", ;
    Format = "D", ;
    Height = 23, ;
    Left = 187, ;
    Top = 2, ;
    Name = "Text2"
  Add Object label1 As Label With ;
    Caption = "Report from", ;
    Height = 17, ;
    Left = 2, ;
    Top = 5, ;
    Width = 67, ;
    Name = "Label1"
  Add Object label2 As Label With ;
    Caption = "to", ;
    Height = 17, ;
    Left = 174, ;
    Top = 5, ;
    Width = 12, ;
    Name = "Label2"
  Add Object command1 As CommandButton With ;
    Left = 293, ;
    Height = 27, ;
    Width = 97, ;
    Caption = "Refresh", ;
    Name = "Command1"
  Add Object list1 As ListBox With ;
    BoundColumn = 4, ;
    ColumnCount = 5, ;
    ColumnWidths = "230,230,145,0,0", ;
    RowSourceType = 3, ;
    Height = 93, ;
    Left = 2, ;
    MultiSelect = .T., ;
    Top = 46, ;
    Width = 636, ;
    BoundTo = .T., ;
    Name = "List1"
  Add Object edit1 As EditBox With ;
    Height = 309, ;
    Left = 2, ;
    Top = 167, ;
    Width = 636, ;
    Name = "Edit1"
  Add Object label3 As Label With ;
    Caption = "Changes", ;
    Left = 5, ;
    Top = 29, ;
    Width = 53, ;
    Name = "Label3"
  Add Object label4 As Label With ;
    Caption = "Details", ;
    Left = 2, ;
    Top = 150, ;
    Width = 41, ;
    Name = "Label4"
  Procedure Init
    Bindevent(This.command1,'Click',This.list1,'Refresh')
    Bindevent(This.list1,'InteractiveChange',This.edit1,'Refresh')
  Endproc
  Procedure text1.Init
    Select Min(Ttod(tTimeStamp)) As MinDate ;
      From ChangeLogEvents ;
      Into Cursor lvwTemp
    This.Value = lvwTemp.MinDate
    Use In lvwTemp
  Endproc
  Procedure text2.Init
    Select Max(Ttod(tTimeStamp)) As MaxDate ;
      From ChangeLogEvents ;
      Into Cursor lvwTemp
    This.Value = lvwTemp.MaxDate
    Use In lvwTemp
  Endproc
  Procedure list1.Init
    This.RowSource = ;
      "select padr(juststem(cobjectname),254) as Object_Name, " + ;
      "cComment, tTimeStamp, iID, mValue from changelogevents " + ;
      "into cursor lvwChanges where between(ttod(tTimeStamp), " + ;
      " thisform.StartDate, thisform.EndDate) Order by tTimeStamp"
  Endproc
  Procedure list1.Refresh
    This.RowSource = This.RowSource
  Endproc
  Procedure list1.InteractiveChange
    Raiseevent(This,'InteractiveChange')
  Endproc
  Procedure edit1.Refresh
    This.Value = lvwChanges.mValue
  Endproc
Enddefine
*
*-- EndDefine: form1
**************************************************

Mi próxima revisión será seleccionar y luego ver las diferencias entre dos modificaciones. He pensado automatizar la característica de comparar documentos de Microsoft Word, pero he estado descontenta en el modo que Office 2003 arruinó, en mi opinión, lo que había sido una característica muy agradable. Y esto, mejor dicho, es una exageración para mi objetivo. Lo invito a que se contacte conmigo en nfolsomNOSPAM@NOSPAMpixeldustindustries.com con cualquier comentario, pregunta o críticas. Sus comentarios serán bienvenidos.

Obsérvese que los eventos de la base de datos fueron agregados en Visual FoxPro 7.0. Usted puede leer más sobre ellos en http://msdn.microsoft.com/library/en-us/dv_foxhelp/html/neconDatabaseContainerEvents.asp.

Nancy Folsom

No hay comentarios. :

Publicar un comentario

Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.