Otras mandangas…

julio 24, 2011 en Código, Consejo, Delphi, Entrada Diario, Mis cosas, Taller práctico

 Dicen diversos diccionarios acreditados que una mandanga, en sentido familiar, es algo así como una excusa, como en “no me vengas con mandangas y ponte a trabajar…”. También puede ser un cuento, chisme o tontería, como “¡oye! ¡siempre sales con mandangas!. Incluso, alguna vez la escuché en un sentido figurado, de persona con ganas de pleito y barullo, como en “¡Mira que te gusta la mandanga!“… :-) Y en el caso de la entrada de mi blog, pienso que anda mas cerca de esa segunda acepción, propia de quien se aburre y sale con relicarios.  :-)

En esta ocasión, he rebuscado en el baul y he subido al servidor un pequeño ejemplo que, aunque no tenga valor en si mismo, por aquello de que fuera exquisito su código,  mezclaba un tanto de todo, y me daba algo de tiempo mientras reviso y buceo en otros temas. Los días van pasando demasiado deprisa y las vacaciones hacen que se huela el descanso y el ocio, lo cual va un poco reñido con el mantenimiento del blog. =:-O 

Además, siendo justos, dado que estamos aqui como quien dice entre amigos, diría verdad si reconociera que me fue util. Pero bueno… mejor que deciros que es o que no es, lo veais vosotros mismos.

Un segundo que prepare unas escenas. Upsssss
 

 ¿Veis…? Al ejecutar el fichero Impresion.exe (el cual queda residente y visible en la barra del sistema) me permitía ir procesando el contenido de unos ficheros de texto plano, de forma que, a medida que iban siendo creados, se leyera su contenido y se dispusiese en una lista de tareas.

Los ficheros siempre tenían la misma extensión. Para nuestro ejemplo los carácteres “sjc”.

En el video, se puede ver como copio al portapapeles uno de los ficheros (concretamente el primero de ellos lanzará la ventana del explorador con la dirección del blog) y lo pego en la carpeta donde reside el ejecutable. Y como resultado sucederá lo comentado. En la vida real, no pegaba ni cortaba ningun fichero sino que lo generaba desde mi aplicación principal, permitiendo delegar esa tarea a esa especie de “servicio”.

Estas eran las lineas que generaban el fichero, cuyo nombre era siempre aleatorio: 

procedure TDemoBlog.VerWebExecute(Sender: TObject);
var 
  Parametros, FFile: String;
begin 
 inherited;

  Parametros:= '11111#Probando un ventana explorer'+
               '#3#1#http://www.sjover.com/delphi#';
  FFile:= Utilidades.PalabraAleatoria(10) +'.sjc';
  Utilidades.SaveFile(FFile, parametros);
...

Y cada fichero generaba una linea de tarea que se mostraba en la ventana de la aplicación. En la imagen inferior podeis ver la ventana en la que existe un sencillo TListbox para mostrarlas.

 Las dos funciones PalabraAleatoria( ) y SaveFile(  ), pueden ser encontradas facilmente en internet con distintas variaciones.  

procedure SaveFile(const FileName: TFileName;
                   const content: string);
begin 
  with TFileStream.Create(FileName, fmCreate) do 
    try 
      Write(Pointer(content)^, Length(content));
    finally 
      Free;
    end;
end;
function PalabraAleatoria(Longitud: integer): string;
const
  Letras = '01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var
 n : integer;
begin
  Result:='';
  for n:=1 to Longitud do
    Result:=Result+Letras[1+Random(Length(Letras))];
end;

 Si tenéis curiosidad por ojear el codigo fuente lo podeis descargar desde el enlace siguiente: 

Descargar fuentes de la entrada

Dentro de las fuentes, leed por favor el archivo leeme.txt donde os comento unas anotaciones necesarias.

Básicamente, existen dos clases: la clase que representa a la tarea a procesar (TTrabajo) y la clase que representa a la cola de tareas (TColaImpresion), con características similares a cualquier abstacción de tipo Cola. Mas abajo podeis ver un extracto del interfaz. La clase TColaImpresion, se apoya en una instancia de la clase TStrings, o lista de cadenas, que suele ser un recurso bastante comun y recurrido, para almacenar las tareas hasta que sean ejecutadas. Y finalmente, dos piezas importantes en el esquema son por un lado el componente Notificador (TShellNotification), que nos ayuda a descubrir que ha sido generado un nuevo fichero, y por otro una instancia descendiente de TTherad que va consumiendo, en un hilo secundario, las tareas de la cola, siguiendo el orden de antiguedad. 

  TColaImpresion = class(TComponent)
      ...
  protected 
    procedure DoAddTaskEvent(ATrabajo: TTrabajo); virtual;
    procedure DoDeleteTaskEvent(ATrabajo: TTrabajo); virtual;
    procedure DoHacerTaskEvent(Sender: TObject; ATrabajo: TTrabajo;
    AModulo, AFuncion: Integer); virtual;
    function DoLinkFileCreateEvent(Sender: TObject;
     Path: String): TShNotificationEvent; virtual;
    procedure Delete(Index: Integer);
  public     { Public declarations } 
    constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;
    procedure Add(const ATextoAMostrar: String; ATrabajo: TTrabajo);
    function Count: Integer;
    function ExtraeItem: TTrabajo;
    procedure VaciarCola;
    procedure UpdateCola;
    procedure DoHacer;
    procedure LeeFicheroImpresion(const AFileName: String);
    property Strings[Index: Integer]: string read Get; default;
    property Path: String read FPath write SetPath;
    property Active: Boolean read FActive write SetActive;
    property OnAddTaskEvent: TNotifyTaskEvent read FOnAddTaskEvent write SetOnAddTaskEvent;
    property OnDeleteTaskEvent: TNotifyTaskEvent read FOnDeleteTaskEvent write SetOnDeleteTaskEvent;
    property OnDoHacerEvent: TNotifyDoHacerEvent read FOnDoHacerEvent write SetOnDoHacerEvent;
  end;

La pregunta que me queda responder es quizás el por qué.   :-)

No… no es que estuviera aburrido… :-)    Habitualmente uno escribe código porque existen nuevas necesidades o requerimientos que satisfacer, en un marco de trabajo mutable y caprichoso. Es lo mas normal. Sin embargo, en ocasiones, esas circunstancias pueden ser simplemente, que estemos encarando la actualización del entorno de desarrollo, y exista -a nuestro pesar- la necesidad de replantearnos el uso en el proyecto de un determinado paquete de componentes por otro que pueda ofrecer mejoras -o sean libres, o sea requerimiento del cliente, etc…-. Así, de la noche a la mañana y sin quererlo ni beberlo, nos veamos obligados a sustituir todo el codigo que afecta a los antiguos componentes por el codigo nuevo y con la necesidad de que el sistema siga funcionando, el nuevo entorno permita compilar sin los paquetes sustituidos y ademas, uno tenga el tiempo de hacer eso sin tener que abandonar el mundo y encerrarse en una cueva… Y eso es otra historia. Y además, por experiencia, amarga…  :-)    

Realmente, estas líneas de codigo las escribí en el contexto de plantearme el cambio del sistema de impresión, de la suit de componentes que utilizaba. El proyecto contenía tropecientos mil informes distintos a cada cual mas intrincado y los generadores de informes, pese a ser muy similares unos de otros, son el peor sitio para perderse cuando uno tiene prisa. 

Así que estas lineas fueron simplemente una estrategia para dilatar ese problema en el tiempo, de forma que fuera llevadero y asumible. Las unidades que iba a extraer del proyecto fueron aisladas dentro del modulo de impresión, de forma que el proyecto compilaba sin las referencias a los paquetes que dejaba, ya en el marco del entorno actualizado. Seguia teniendo el problema de la migración, porque eso no me lo resolvía, pero sí podía compilar e iniciar la sustitución progresiva de las misma. Por otro lado, tampoco era tan mala idea. He visto otras aplicaciones trabajar así. El servidor de fax de la empresa tiene un sistema similar: Habilita una carpeta compartida al alcance de todos los clientes de fax del sistema. y para enviar un fax, son creados simultaneamente un documento de texto que parametriza el envío y otro documento que representan los datos a enviar. Y poco mas. El servidor de fax se limita a monitorizar la carpeta y va procesando los ficheros anotando el exito o fallo de la operación de envío.

En este caso simplemente habia reservado una carpeta local para depositar los ficheros que representaban cada informe. Seguro que se os ocurren algunos detalles más (que un fichero de texto plano se puede compartir, que la aplicación se comparte con cualquier otra aplicación que sea capaz de escribir en un fichero de texto, y que además, se centraliza el uso de los recursos de impresión, siendo susceptible de monitorizar el consumo del mismo, etc, etc…).

Nada mas por hoy. Espero que estas lineas os puedan ser de alguna utilidad.  

 

 

Simplifica tu código Delphi…

marzo 30, 2010 en Código, Consejo, Delphi, Enlace interesante, Entrada Diario

Simplifica tu código, piensa en clases, abstrae y racionaliza, usa el sentido común, etc… son algunos de los lemas que hemos podido compartir durante muchos de los artículos anteriores, casi desde siempre, con mas o menos acierto. Valga la redundancia, casi diría que en realidad, es una preocupación cuasi universal que nos corroe, a medida que avanzamos y aprendemos y nos formamos. También de alguna forma, exteriorizamos esos pensamientos en muchos de los post que acabamos publicando.

Nuestro punto de parada hoy, es nuevamente el blog de Stefaan Lessage, y la parada es para compartir cuatro artículos que ha escrito durante el mes de marzo y que pienso que forman parte de esa idea general que siempre hemos intentado plasmar: pensar en clases y abstraer. Pienso que la lectura de las cuatro entradas de Stefaan es muy aconsejable  y si bien, puede resultarnos mas o menos incomodo que esté escrita en otro idioma (ese punto ya depende de cada uno), existe el suficiente código para que pueda entenderse el trasfondo y la enseñanza que aporta. Sobretodo, os la aconsejo si os estáis iniciando en el entorno y buscais patrones de razonamiento que os sirvan de referencia en vuestros desarrollos.

La serie se titula, como no, “Simplify your Delphi Code using some basic rules, OO techniques and some refactoring“. Mas o menos, “Simplifica tu codigo Delphi usando algunas reglas básicas, tecnicas orientadas a objetos y refactorización”.

Parte 1

En la primera parte de la serie, Stefaan Lessage presenta el problema o la cuestión. Es un capítulo introductorio y breve, en el que inicia su reflexión imaginando una necesidad que suele darse con frecuencia y que consiste en retomar código escrito años atrás para su revisión. Puede que nos toque enfrentarnos a código difícil de leer o entender y que requiera un nuevo enfoque para facilitar en un futuro su mantenimiento.

Al hilo de mostrarnos las ventajas, Stefaan se centra con un ejemplo que puede ser representativo de esa ganancia y que va a servir de eje sobre el que van a girar el resto de los artículos de la serie. Personalmente, creo que ha elegido un ejemplo que recoge una necesidad básica en cualquier aplicación: guardar y cargar datos de personalización. Y como imagináis, casi siempre van a estar implicados o bien archivos ini o el registro, ficheros xml, etc.

Parte 2

La segunda parte, una vez planteada esa introducción, se nos muestra algo de código. Código que podría representar a esas lineas que deberíamos revisar, bajo un nuevo enfoque al hilo de la programación orientada a objetos. Siguiendo con su ejemplo, muestra varios procedimientos que permitirían desde los eventos de creación y destrucción del formulario, cargar y guardar los valores de personalización. Lógicamente, junto con estas lineas de código se nos presentan las desventajas de estas decisiones y los problemas que pueden suponer en distintos escenarios.

Parte 3

Aquí ya entra Estefaan Lessage en el primer acercamiento a resolver el problema desde la perspectiva OOP. Para ello, crea una clase base que permita tratar distintos tipos de datos de forma unificada. Así pues, la clase TdvSetting realmente no hace nada mas que preparar el camino. De hecho, los metodos Get/Set se limitan unicamente a generar una excepción, puesto que ella no sabe realmente como debe responder a dichos métodos. Será sus descendientes los que sobrescriban los métodos adecuados al contexto de la aplicación, creando código especifico.

Parte 4

Y ya la parte cuarta, finaliza la serie. En este artículo se define la clase TdvStringSetting a modo de ejemplo, descendiente de TdvSetting, y se nos proponen nuevas clases descendientes (TdvInteggerSetting, TdvBooleanSetting, etc.) y una clase adicional TdvSettings, descendiente de TObjectList, al modo de contenedor. Fijaros en un detalle importante, esta lista de objetos, recibe como parámetros en su funciones y procedimientos la clase base, jugando de esa forma Steffaan Lessage con la herencia y el polimorfismo (si no recuerdo mal a este tipo de polimorfismo se denomina como Polimorfismo de subtipado o de inclusión).

Creo que vale la pena que perdáis unos minutos y reviséis el código ya que este tipo de razonamientos pueden aportar un valor añadido al código que escribimos. No es tan importante la cantidad de lineas que uno puede abarcar en una jornada frente a que éstas garanticen que pueda ser revisadas meses después al aire de un nuevo contexto o en respuesta de nuevos requerimientos. Calidad o cantidad… Siempre debemos “aspirar” a la calidad.

Comparto plenamente lo escrito por Stefaan en la introducción

Think before you start writing your first line of code…

Una verdad como un templo.

Shifting TFields in TDataSets Bound to TDBGrids…

marzo 24, 2010 en Advertencia, Artículos, ¿Sabías que...?, ¿Sabías que...? [Delphi], Consejo, Delphi, Entrada Diario, traducciones

Esta vez, he seleccionado uno de los artículos del blog de Cary Jensen que me ha parecido especialmente interesante para compartirlo con vosotros. En el artículo, Cary Jensen comenta con sus lectores, un posible bug ocasionado por el comportamiento de los campos persistentes del DataSet (luego se verá en el articulo, comentado y reflexionado por el, que no es tanto un error de código). De cualquier forma, sí me ha parecido interesante pues es algo que debería tenerse en cuenta.

Os explico:  Accidentalmente, se da cuenta que al ser movidas las columnas de una rejilla de datos, una facultad que tiene per se de forma automática al ser creadas, el orden en los campos del dataset es alterado. ¿En que casos? Bueno, esto solo sucede cuando no existen los campos persistentes en el componente TDBGrid y puede generar errores si hemos referenciado llamadas a los campos persistentes del dataset mediante el array TField usando el indice de acceso. Cary, a tenor de lo visto reflexiona en la entrada sobre este punto y las posibles soluciones.

Yo tampoco creo que sea un bug. Aunque particularmente a mi sí me hubiera parecido acertado bloquear la funcionalidad de mover las columnas y solo permitirla en el caso de que hubieran sido creadas, de forma que no hubiera podido existir ese potencial error. Pero bueno… es mi opinión.

Ahh. Como siempre comento, perdonad los posibles errores en la traducción que he intentado que fuera lo menos literal posible.

Vamos a ello:

Shifting TFields in TDataSets Bound to TDBGrids: A Potential Source of Bugs in Your Code

Autor: Cary Jensen – Enero 26, 2010

http://caryjensen.blogspot.com/2010/01/shifting-tfields-in-tdatasets-bound-to.html

He estado trabajando con Delphi desde el principio, con particular énfasis en el desarrollo de bases de datos. Como resultado de ello, no es demasiado frecuente que yo encontrase un comportamiento de los componentes relacionados con los datos que me sorprendiera. Bien. Ha sucedido así en el último mes. Y lo que yo observé  puede ser el origen de errores potencialmente desastrosos, aunque infrecuentes, en un gran numero de aplicaciones de bases de datos con Delphi.

Esto es lo que yo pude observar: Los TFields en un TDataSet abierto, cambiaron de orden en tiempo de ejecución. Especificamente, un TField que estaba originalmente en la posicion cero (DataSet.Fields[0]), en el momento en el que se habia creado el TDataSet, estaba en una posición diferente en el array de campos un pequeño tiempo después. Yo descubrí este comportamiento cuando una excepción fue lanzada como resultado de mi intento, mediante código, de leer el valor entero del campo de tipo TIntegerField que yo habia creado en la primera posición del array de campos de mi TDataSet. Desde el momento en el que yo había creado el TDataSet y la ejecución de mi código, el campo integer habia sido movido de posición.

Lo que habia sucedio no era mágico. Los campos no pudieron cambiar la posición por ellos mismos, ni lo hicieron en algo basado en mi código. Lo que causó que los TFields cambiaran físicamente su posición en el TDataSet fue que el usuario había cambiado el orden de las columnas en el TDBGrid, el cual estaba vinculados al TClientDataSet (a través del componente TDataSource, por supuesto). La habilidad del usuario para cambiar de posición las columnas en un TDBGrid, por cierto, es el comportamiento por defecto en el componente.

Además de ser interesante (He de suponer que una vez que se abrió conjunto de datos, la posición de la “TFields” en la matriz de Campos ya estaba fijada), este comportamiento es la fuente potencial de excepciones intermitentes, el tipo de las que son particularmente difíciles de localizar. Resulta que este comportamiento, que nunca he visto descrito antes, ha existido desde Delphi 1. (En realidad, he observado este efecto en Delphi 7, Delphi 2007 y Delphi 2010. Sin embargo, entiendo que la fuente subyacente de este comportamiento ha existido desde Delphi 1, aunque no lo he confirmado expresamente.)

He creado una aplicación muy sencilla para demostrar este efecto. Consiste en un único formulario en el que existe un TDBGrid, un TClientDataSet y un componente TButton. El ClientDataSet esta enlazado al TDBGrid a traves del TDataSource. En el evento OnCreate del formulario aparece algo como lo siguiente:

procedure TForm1.FormCreate(Sender: TObject);
begin
  with ClientDataSet1.FieldDefs do
  begin
    Clear;
    Add('StartOfWeek', ftDate);
    Add('Label', ftString, 30);
    Add('Count', ftInteger);
    Add('Active', ftBoolean);
  end;
  ClientDataSet1.CreateDataSet;
end;

Button1, está marcado con la etiqueta “Show ClientDataSetStructure” y contiene el siguiente código en el evento OnClick.

procedure TForm1.Button1Click(Sender: TObject);

var
  sl: TStringList;
  i: Integer;
begin
  sl := TStringList.Create;
  try
    sl.Add('The Structure of ' + ClientDataSet1.Name);
    sl.Add('- - - - - - - - - - - - - - - - - ');
    for i := 0 to ClientDataSet1.FieldCount - 1 do
    sl.Add(ClientDataSet1.Fields[i].FieldName);
    ShowMessage(sl.Text);
  finally
    sl.Free;
  end;
end;

Para demostrar el movimiento de los campos, ejecuta la aplicación, haciendo click sobre el botón marcado con la etiqueta Show ClienteDataSet Structure. Tu debería ver lago semejante a lo que muestra la figura 1.

Figure 1

Luego, arrasta algunas columnas del DBGrid y cambia el orden de los campos. Vuelve a hacer click en el botón “Show ClientDataSet Structure”. En ese momento veras algo similar a lo mostrado en la Figura 2.

Figure 2

Lo remarcable de este ejemplo es que la posicion de los TFields en el propiedad Fields del TClientDataSet cambió, de forma que el campo que estaba  en la posición ClientDataSet.Field[0] en un momento determinado no necesariamente está en un momento posterior. Y desafortunadamente, esto no es responsabilidad del TClientDataSet. He realizado la misma prueba con TTables basadas en el bde y TAdoTables basadas en Ado y se obtuvo el mismo efecto.

Contribuyen al resultado de este comportamiento tres factores. Esto son:

  • Un TDBGrid conectado a un DataSet a través de un DataSource
  • El TDBGrid permite al usuario mover las columnas en tiempo de ejcución
  • Las columnas del TDBGrid son dinámicas; significa esto que son creadas por el TDBGrid en ejecución.

Si tú mediante código haces referencia a los campos del DataSet conectados al TDBGrid, y existen las tres condiciones precedentes usando un indice, tu aplicación puede lanzar una excepción, o producir resultados incorrectos, si el usuario mueve una o mas columnas en ese TDBGrid. En la siguiente sección, considerare algunas soluciones para resolver este problema, asi como compartir con Ustedes la razón de ello.

Existen varias soluciones

Existen varias tácticas que puedes usar para eliminar este potencial bug de vuestras aplicaciones. La primera es definir la TColumns de tu TDBGrid usando campos persistentes.

Crear columnas persistentes, puede ser hecho tanto en tiempo de diseño como de ejecución. Para hacerlo en tiempo de diseño, basta añadir las TColumns usando el editor de Columnas. Éste, se muestra haciendo click con el boton derecho del ratón sobre el TDBGrid y seleccionando el Editor de Columnas o bien haciendo click en la ellipsis de la propiedad Columns del TDBGrid en el Inspector de Objetos. Si tu DataSet está Activo, tú puedes hacer click en el botón “Add All Fields” en la toolbar del Editor de columnas.  O bien, añadir uno o mas TColumns y fijar la propiedad FieldName en el Editot de propiedades.

Para crear columnas en tiempo de ejecución, puedes usar los métodos Add o Create de la propiedad Columns del TDBGrid. Puedes fijar los valores de propiedades especificas de las columnas añadidas o creadas.

La segunda solucion,  aunque tiene algunas consecuencias negativas, previene que el usuario mueva las TColumns del TDBGrid. Esto puede ser hecho eliminando el flag dgResizeColumn de la popriedad Options del TDBGrid. Mientras este enfoque es efectivo, elimina opciones del interfaz potencialmente valiosas. Además, eliminando el flag no solo se restringe la opción de reordenar las columnas sino que impide redimensionar el ancho de las columnas. (Para aprender como limitar reordenar las columnas sin eliminar la opción de cambiar el tamaño de la columna ver el articulo de Zarko Gajic How to allow column resize by disable movement (in TDBGrid).

Una tercera solución es evitar referenciar a los TFields del TDataSet basandonos en un índice literal en la propiedad de tipo array Fields( ya que esta es la esencia del problema). En otras palabras, si tu necesitas acceder al campo Count, definido en el codigo de ejemplo que precede, no uses ClientDataSet1.Fields[2].  En la medida que tu conozcas el nombre del campo, puedes usar algo como ClientDataSet1.FieldByName(‘Count’).

Sin embargo existe una gran desventaja en el uso de FieldByName. En concreto, este metodo identifica el campo iterando a traves de la propiedad Fields del TDataSet, buscando  una concidencia basad en el nombre del campo. Desde el momento en que hace esto cada vez que se invoca FieldByName, deberias evitarlo en situaciones donde el campo necesita ser referenciado muchas veces, como sucede en un bucle que recorre un TDataSet muy extenso, con muchos campos.

Si tu necesitas apuntar al campo repetidamente (y en un gran numero de veces) considera el uso de algo como el siguiente fragmento de código:

var
CountField: TIntegerField;
Sum: Integer;
begin
 Sum := 0;
  CountField := TIntegerField(ClientDataSet1.FieldByName('Count'));
  ClientDataSet1.DisableControls; //assuming we're attached to a DBGrid
  try
    ClientDataSet1.First;
    while not ClientDataSet1.EOF do
    begin
      Sum := Sum + CountField.AsInteger;
      ClientDataSet1.Next;
    end;
  finally
    ClientDataSet1.EnableControls;
  end;
end;

La cuarta solucion es el uso del método FieldByNumber de la propiedad Fields del TDataSet. Si ya tienes código escrito que usa un indice para el array Fields, y trabaja correctamente, siempre y cuando el usuario no mueva las columnas del TDBGrid, esta es otra solucion. Cambia tu codigo para usar FieldByNumber.

Hay dos aspectos interesantes para el uso de FieldByNumber. Primero tu debes cualificar su referencia con la propiedad Fields de tu DataSet. Segundo, al contrario del array Fields, que es basado en indice cero, FieldByNumber  se inicia en 1 para indicar la posición del campo que tu quieres referenciar.

La siguiente es una versión actualizada del manejador del evento OnClick de Button1, mostrado anteriormente, que usa el método FieldByNumber.

procedure TForm1.Button1Click(Sender: TObject);

var
  sl: TStringList;
  i: Integer;
begin
  sl := TStringList.Create;
  try
    sl.Add('The Structure of ' + ClientDataSet1.Name +
       ' using FieldByNumber');
    sl.Add('- - - - - - - - - - - - - - - - - ');
    for i := 0 to ClientDataSet1.FieldCount - 1 do
    sl.Add(ClientDataSet1.Fields.FieldByNumber(i + 1).FieldName);
    ShowMessage(sl.Text);
  finally
    sl.Free;
  end;
end;

Para el ejemplo propuesto, el código produce el siguiente resultado, sin considerar la posición de las columnas en la rejilla asociada. Esto se puede ver en la Figura 3

Figura 3

Hay una quinta solución, pero solo está disponible cuando tu TDataSet es de la clase TClientDataSet, como el que existe en mi ejemplo. En esas situaciones, tu puedes crear un clon del TClientDataSet original, y mantener la estructura original. En consecuencia, cualquiera que sea el campo que originalmente aparezca en la posicion zero, seguira estando en la misma posición, con independencia de que haya podido hacer el usuario al TDBGrid que muestra los datos del TClientDataSet.

Notar que no estoy sugiriendo que  debas referenciar los campos TFields del TDataSet usando literales enteros. Personalmente, el uso de un variable de tipo TField, que se inicializa a través de una llamada a FieldByName es más legible e inmune a los cambios en el orden físico de la estructura de la tabla(¡aunque no sea inmune a los cambios en los nombres de tus campos!) .

Para terminar

Hay un par de puntos finales que quisiera hacer. Primero, la actual estructura subyacente no es afectada. Especificamente, si despues de cambiar el orden de las TColumns en un TDBGrid, llamas la método SaveToFile de la clase TClientDataSet enlazado a ese TDBGrid, la estructura guardada es la original (la verdadera estructura interna). De forma similar, si tu asignas la propiedad Data de un TClientDataSet a otro, el TClientDataSet destino tambien muestra la estructura verdadera (lo cual es similar al efecto observado cuando un origen de datos TClientDataSet es clonado).

De igual forma, cambios en el orden de las columnas de TDBGrids enlazados a otros probados TDataSets, incluyndo TTable y ADOTable, no afectna a la estructura interna de las tablas. Por ejemplo, un TTable que muestra datos desde la tabla de ejemplo de Paradox customer.db que viene con Delphi no cambia esta estructura de la tabla en disco (ni lo esperarias).

El segundo punto es que esto no es un bug en cualquiera de la clases TDataSet o TDBGrid (o TColumn o TField). Es asi como esas clases han sido diseñadas para trabajar. Y aunque este comportamiento introduce errores en tus aplicaciones, esto es porque nosotros no hemos cuidado ésto hasta el momento. Y, tu ahora conoces como se comportan tan bien como para prevenir que causen excepciones en tus aplicaciones con Delphi.

El punto final viene a nosotros por el usuario Sertac Akyuz desde StackOverflow, quien respondió a la pregunta acerca de este comportamiento que yo publiqué en ese sitio Web. Yo había revisado las fuentes tanto para la clase TDataSet como para la clase TDBGrid y no pude localizar el origen del mismo. Sertac escribió que este comportamiento es escontrado actualmente en las clases TColumns y TFields. Especificamente, cuando cambia la posicion de la columna de una TColumn dinámica (no persistente), fruto de una llamada al metodo que fija la propiedad Index de TField, lo cual afecta a la posición de ese campo en la propiedad Fields del TDataSet.

Ahora tu sabes que este potencial problema existe, bajo que condiciones puede emerger, también como sus efectos, deberias ahora echar un vistazo a tus aplicaciones para ver si tienes TDBGris con TColumns no persistentes que el usuario pueda mover en tiempo de ejecución. Si además, referencias las campos TFields con esas TColumns usando indices literales a la propiedad Fields del TDataSet, puedes eliminar errores potenciales resultado de apuntar a un campo erroneo en tiempo de ejecución usando una de las soluciones que yo he apuntando anteriormente en este artículo.

Copyright (C) 2010 Cary Jensen. All Rights Reserved

Release Notes: Delphi and C++Builder 2010 Updates 4, 5 (Database Pack), and Boost Library

diciembre 16, 2009 en Advertencia, Ayuda, Consejo, Delphi, Entrada Diario, Noticias, Noticias Delphi

Han llegado los primeros comentarios de la disponibilidad de los últimos updates para Delphi 2010, C++ Builder 2010 y Rad Studio. Yo lo he podido leer por el comentario de Andreano Lanusse desde el grupo Twibes Hispano y tal y como nos dice, podeis acceder facilmente al mismo tan solo con la opción de chequear actualizaciones (Check for Updates) existente en el item creado por el entorno en el menu de inicio.

Logicamente, tambien podeis acceder a la descarga a través del registro en la zona de descargas de Embarcadero.

Es muy importante que leais las notas de instalación antes de efectuar la actulización. Estas notas las podeis leer en la entrada de Kris Houser

http://edn.embarcadero.com/article/40174#14KnownIssues

 No dejeis de leerla, ya que existen algunos prerrequisitos que afectan al orden de instalación, tanto de los updates que contiene (segun el producto) como respecto a los anteriores, que puedan estar pendientes de instalación.

En mi caso concreto, parece ser que finalizó con exito… pero he podido ver durante la actualización de la instalación del update 5, numerosas advertencias similares a la que se muestra en la imagen:

Advertencia instalacion databasepack (Update 5)

Que permitieron seguir la instalación y como comento, aparentemente finalizar con exito…

Ni idea… :-(

El problema de estas advertencias es que no sabes tras aceptar, si han sido “resueltas” por el mismo instalador o ha quedado algo incorrecto.  :-)   

En el menú de ayuda del entorno, se visualizan los updates pero supongo que tendré que buscar la forma de asegurarme de que realmente la instalación fue correcta. Lo peor de todo es que en las notas de instalación, se comenta que la desistalación de estos updates puede ser problematica… Por eso os comento que leais con atención las notas de instalación.

__Uninstalling Update 4
If you need to uninstall Update 4, you must uninstall the entire product and reinstall one of the following:
  • Delphi 2010, C++Builder 2010, or RAD Studio 2010 (RTM)
  • Update 1 to Delphi 2010, C++Builder 2010, or RAD Studio 2010

    Uninstalling Update 5 (Database Pack)

If you need to uninstall the update, select “Embarcadero Delphi and C++Builder 2010 Database Pack” in Add/Remove Programs, and click Remove. The IDE has dependencies on the Database Pack, so you should not uninstall the DataBase Pack if you will continue to use the IDE.

If you have all three personalities of RAD Studio 2010 installed (including Delphi Prism), uninstalling Update 5 (Database Pack) or uninstalling Delphi Prism will mean that you must restart the Blackfish SQL server.

    Uninstalling the Boost Library

The C++ Standard Library has dependencies on Boost, so you should not uninstall Boost if you will continue to use the C++ compiler.
 

Y por ultimo, quizás dejar el comentario que me ha causado extrañeza que tras finalizar la actualización, fuera necesario registrar de nuevo el producto.

En fin… dejo el comentario por si quereis añadir algo. Ya sabeis que sois libres de añadir lo que gusteis.

Consumir un web service (DelphiAccess)

septiembre 27, 2009 en Consejo, Delphi, Enlace interesante, Entrada Diario

En uno de los comentarios de una entrada anterior, compartía con Eliseo Gonzalez, que me había parecido muy interesante uno de los tutoriales de  Delphi Access. Como muchos sabeis Eliseo forma parte de esa comunidad. Iba a decir nacida en Mexico, pero actualmente, la verdad es que poco importa que nazca en un pais o en otro, cuando abren sus puertas a una pluralidad de programadores que excede de ese ámbito nacional.

En la barra lateral de enlaces podéis encontrar el blog de Eliseo, (La Turbo señal). Pero no venía esta entrada a cuento del blog, que os anticipo que tiene puntos que os serán de utilidad, sino del foro que mantienen dentro del dominio de DelphiAccess.

Areas como OOP, Api, Graficos, lenguajes y entornos (Delphi Prism, C#, C++Builder, etc.), bases de datos (Access, Firebird, MySql, Oracle, etc.),  cubren buena parte de las necesidades cotidianas, por lo que pienso que se puede recomendar abiertamente y que os sacará en mas de alguna ocasión de apuros.

Aunque tengo que comentar que una de las cosas que mas me ha gustado es ver que tiene, dentro de uno de los foros abiertos a los tutoriales, algunos que están muy trabajados, con una cantidad de detalle y de imagenes grande. Muy practicos.

Por poner un ejemplo, el que han abierto con el asunto: “Consumir webservice con acceso a base de datos”

 Ir al enlace 

Os recomiendo que le deis un vistazo, ya que es muy completo.

Y como ese existen una veintena de tutoriales similares, además de manuales o componentes y trucos.

Nada mas. Enviar un saludo a esta comunidad y desearles mucho ánimo y que sepan dar continuidad al proyecto.

¿Ya se puede ver la página? :-)

julio 9, 2009 en Consejo, Entrada Diario

A las personas confiadas, habitualmente le suceden problemas propios de esa ingenuidad.  :-)

El caso es que hoy he tenido un pequeño problema con el blog porque alguien se ha entretenido en inyectar codigo malicioso en uno de los scripts que inician la carga. Por lo que he tenido que dedicar un rato a ver cual era el problema y tras contactar con el proveedor del alojamiento, se ha solucionado en un plis plas. En ese sentido, siempre he valorado como muy bueno el soporte de Dinahosting y tras varios años alojando la pagina en sus servidores no me han dado un solo motivo para pensar en elegir otro. Todo lo contrario.

Que le pasen a uno estas cosas es hasta cierto punto bueno. Los blogs siguen teniendo problemas de seguridad; y habilitar el administrador del blogs para la modificación tanto de las hojas de estilo como de scripts php propios del tema, etc…, puede dejar una puerta abierta a que se pueda sobrescribir algun que otro archivo. Es bueno que le pasen a uno estas cosas pero cansa.

Lo peor de todo, es que el tiempo ese que dedicas a revisar que nadie pueda alterar maliciosamente el contenido se lo estás quitando a las personas que lo han podido encontrar válido y a los que has podido en algun momento ser de ayuda. Yo no saco nada de mantener el blog. Absolutamente nada. No hay ni un solo ingreso economico ni nunca lo habrá, con independencia de que tenga 3000 visitas al mes o 300000.  Cada uno tiene sus ideas. y desde principio fue así y  si no puede ser, porque llegue un momento en el que no lo pueda mantener, pues seguro que cualquiera de los amigos y compañeros que he podido tener no tendran problema en alojar su contenido. Aunque no se vea ni se valore, mantener actualizado el contenido es una tarea que te saca muchas horas semanales, y se hace aun cuando se las tenemos que quitarselas a otras areas personales.

En fin, no veo que gana nadie dañando el blog. Ni este ni otros muchos…

El espiritu de los desarrolladores es el de construir. Solo destruimos para generar un proyecto mejor.