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.