Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Otros temas > Trucos
Registrarse FAQ Miembros Calendario Guía de estilo Buscar Temas de Hoy Marcar Foros Como Leídos

Los mejores trucos

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 30-06-2006
Avatar de marcoszorrilla
marcoszorrilla marcoszorrilla is offline
Capo
 
Registrado: may 2003
Ubicación: Cantabria - España
Posts: 11.221
Poder: 10
marcoszorrilla Va por buen camino
Tablas en memoria

A veces olvido que existen dos versiones de Delphi además de la cliente/servidor. Comienzo a explicar técnicas para acelerar el trabajo con campos de búsquedas, o para hacer más robusta la entrada de datos master/detail, y propongo el uso de conjuntos de datos clientes (TClientDataSet). Pero Delphi Standard y Professional no tienen este componente.

Las tablas en memoria del BDE

Por fortuna, el BDE ofrece una técnica relativamente sencilla para suplir esta carencia: las tablas en memoria. Una tabla en memoria se crea mediante la siguiente función del API del BDE:
Código Delphi [-]
function DbiCreateInMemTable(hDb: hDBIDb; pszName: PChar; iFields: Word;
   pfldDesc: pFLDDesc; var hCursor: hDBICu): DBIResult;
El primer parámetro es un handle a una base de datos; hace falta un alias para ubicar dentro del mismo a la tabla. pszName indica el nombre que le daremos a esta tabla; aunque, en realidad, no sé para qué es necesario este nombre. iFields es la cantidad de columnas que va a tener la tabla, y pfldDesc apunta a un array de valores del tipo FLDDesc, que contiene la descripción de las columnas. Por último, hCursor recibirá el handle del cursor de la recién nacida.

Hay varias limitaciones aplicables a este tipo de tablas. La más importante es que no se pueden definir índices sobre las mismas. Además, tampoco el BDE soporta directamente restricciones, ni valores por omisión, sobre sus columnas. En este sentido, TClientDataSet es mucho más potente. Además, no existe un mecanismo directo de guardar su contenido en un fichero plano, a no ser que utilicemos la función DbiBatchMove y un fichero ASCII como destino.

Encapsulando la tabla

Conozco pocos kamikazes dispuestos a trabajar directa y exclusivamente con el API del BDE. Si queremos aprovechar las tablas en memoria del BDE, debemos crear un componente que las haga "digeribles". Aquí explicaré los pasos básicos de esta técnica, y el código fuente completo puede encontrarse en la página de ejemplos.

Crearemos una nueva clase, TMemoryTable, cuyo ancestro será TDBDataSet. ¿Por qué esta clase? Pues porque necesitamos un alias abierto para crear el cursor de la tabla, y nos ahorraremos bastante código si aprovechamos la clase mencionada, que implementa las propiedades DatabaseName, Database y DBHandle; esta última propiedad es la encargada, además, de realizar la conexión con el alias cuando tratamos de recuperar su valor.

Como consecuencia indeseada de la elección de este ancestro, la clase TMemoryTable publicará las propiedades Filter, Filtered y FilterOptions, aunque realmente no estén correctamente implementadas.

Tenemos que redefinir la función CreateHandle, para obtener un cursor a la tabla en memoria; será en esta función donde llamaremos a DbiCreateInMemTable. Pero antes tenemos que decidir cómo configuraremos las columnas de la nueva tabla. Utilizaremos dos técnicas:

La nueva propiedad CloneDataSet puede apuntar a una tabla o consulta, de la cual se extraerá las definiciones pertinentes.
Aunque el tipo TDataSet ya contiene la propiedad FieldDefs, no la publica en el Inspector de Objetos. De esto nos encargaremos nosotros, además de implementar la función IsFieldDefsStored que utilizaremos en una cláusula stored para almacenar el valor de FieldDefs en el fichero DFM cuando el número de definiciones de campos sea distinto de cero.
Entre estos dos mecanismos, CloneDataSet tendrá más prioridad.

El núcleo de todo el componente es, como he mencionado, la función CreateHandle, cuya implementación incluyo a continuación:
[delphi
function TMemoryTable.CreateHandle: HDBICur;
var
OldActive: Boolean;
DSProps: CurProps;
FieldDesc: TFieldDescList;
I, FieldCount: Word;
begin
FieldCount := FieldDefs.Count;
if FCloneDataSet <> nil then
begin
OldActive := FCloneDataSet.Active;
try
FCloneDataSet.Open;
Check(DbiGetCursorProps(FCloneDataSet.Handle, DSProps));
FieldCount := DSProps.iFields;
SetLength(FieldDesc, FieldCount);
Check(DbiGetFieldDescs(FCloneDataSet.Handle, pFLDDesc(FieldDesc)));
finally
FCloneDataSet.Active := OldActive;
end;
end
else if FieldCount > 0 then
begin
SetLength(FieldDesc, FieldCount);
for I := 0 to FieldCount - 1 do
with FieldDefs[i] do
EncodeFieldDesc(FieldDesc[i], Name, DataType, Size, Precision);
end
else
DatabaseError('Dataset required');
Check(DbiCreateInMemTable(DBHandle, PChar(FTableName), FieldCount,
pFLDDesc(FieldDesc), Result));
end;
[/delphi
El método auxiliar EncodeFieldDesc ha sido copiado de la clase TTable, y transforma un TFieldDef en un valor de tipo FLDDesc, del API del BDE, haciendo uso de algunas constantes globales de la unidad DBCommon:
[delphi
procedure TMemoryTable.EncodeFieldDesc(var FieldDesc: FLDDesc;
const Name: string; DataType: TFieldType; Size, Precision: Word);
begin
with FieldDesc do
begin
StrPLCopy(szName, Name, SizeOf(szName) - 1);
iFldType := FldTypeMap[DataType];
iSubType := FldSubTypeMap[DataType];
case DataType of
ftString, ftFixedChar, ftBytes, ftVarBytes, ftBlob..ftTypedBinary:
iUnits1 := Size;
ftBCD:
begin
{ Default precision is 32, Size = Scale }
if (Precision > 0) and (Precision <= 32) then
iUnits1 := Precision
else
iUnits1 := 32;
iUnits2 := Size; {Scale}
end;
end;
end;
end;
[/delphi
Un mundo de posibilidades

Esta misma técnica (crear descendientes de TBDEDataSet redefiniendo simplemente CreateHandle) puede aplicarse a muchos otros tipos de cursores especiales del BDE. Por ejemplo, existe una larga lista de funciones, que puede buscar en la ayuda del BDE tecleando "DbiOpenList functions", y que devuelven información sobre tablas disponibles, la estructura de las mismas, los alias y controladores registrados, etc. Lo más interesante consiste en que, una vez que redefinimos CreateHandle, el resto de los métodos heredados desde TBDEDataSet y TDataSet se encargan de la configuración del buffer del conjunto de datos, de la creación de componentes TFields y todo lo demás. Quizás más adelante muestre ejemplos de clases que utilicen estas otras funciones.

Nota: ¿Ha escuchado o leído, alguna vez, el vocablo procrastination? Consulte la página 252 del libro "Delphi Component Design", de Danny Thorpe (ISBN 0-201-46136-6).
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 14:58:09.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi