PDA

Ver la Versión Completa : Que no se abran automaticamente las conexiones


guillotmarc
03-07-2003, 01:23:11
Hola amigos.

Los objetos de conexión tienen la desagradable costumbre de activarse cuando se trabaja en tiempo de diseño con sus datasets.

El problema es que me gustaría que al arrancar la aplicación, no se abran las conexiones, aunque tengan la propiedad Active a True.

(NOTA : Esto lo quiero para una pequeña aplicación de mantenimiento, donde no sé donde está la base de datos, y por lo tanto si se intenta abrir en el arranque fallará el arranque, hay que esperar a que el usuario seleccione la base de datos)

Ya sé que una buena solución es cerrar las conexiones antes de compilar el proyecto, pero cuando posteriormente haces una modificación, muchas veces me olvido de desactivar las conexiones. Con lo que solo me doy cuento cuando estoy en casa del cliente, y no puedo arrancar la aplicación.

Además hay que verificar que todos los DataSets también estén cerrados, puesto que si se deja alguno abierto, entonces al arrancar el programa también abrirá la conexión (independientemente de que tuviera el Active a False).

Lo primero que viene en mente, es poner la propieadad Active a False, en el Create del DataModule. Pero ya es demasiado tarde, antes de llegar allí ya ha intentado abrir la conexión. También he probado ejecutando un Abort en el OnBeforeConnect, pero esto no solo aborta la apertura de la conexión, sinó todo el proceso de arranque de la aplicación.

¿ Se os ocurre alguna idea ?

Saludos.

Ruben_Cu
03-07-2003, 02:46:11
Hola Marc, además de las que señalas otra posibilidad sería creando el Datamodule como primer objeto en tu proyecto, o sea, pasandolo a ser la primera forma que creas, no pienso que antes de esta acción hayas intentado conectarte.
Claro la mas sencilla sería la que comentas, de cerrarlas todas en tiempo de diseño pero... no te gusta.
Saludos

guillotmarc
03-07-2003, 11:26:36
Hola.

Gracias por responder. El problema es que quiero esperar a que el usuario pueda localizar la base de datos (en un RxFilenameEdit).

Por lo tanto, no me sirve crear primero el formulario, puesto que durante el proceso de carga se crea posterirormente el datamodule y intenta abrir las conexiones.

Lo que querría es interceptar en algún evento el proceso de apertura de las conexiones, y cancelarlo.

Un saludo.

andres1569
03-07-2003, 14:47:48
Hola:

Guillotmarc, ¿verías con buenos ojos una solución en la que se desactivaran todos los Datasets y el Database antes de guardarse en el DFM? Es decir, lo que tu haces "a mano" antes de compilar, que se hiciera de forma automática.

Digo esto porque donde se disparan todas estas conexiones es justo después de tomar los valores del .DFM; si un DataSet / Database / ... se guarda con Active=TRUE, se abrirá en el método Loaded, antes de cualquier OnCreate. Ya que el BeforeConnect no aprovecha (también he probado lo de Abort y alguna cosa más pero ya resulta tarde para ir cerrando Datasets asociados); sólo se me ocurre que en el DFM no conste Active como TRUE. La pega es que eso pasa por crear un ancestro de DataModule que sobreescriba el método WriteState, meterlo en un Package y derivar de él los DataModules de nuestro programa. (aunque se puede cambiar fácilmente los DataModules que ya existan sin tener que rehacer casi nada).

Voy a hacer una prueba, si te sirve te envío lo que haga.

guillotmarc
03-07-2003, 17:49:05
Hola Andrés.

Me parecería perfecto alguna forma automatizada de cerrar todos los DataSets y objetos Connections, antes de compilar. Aunque realmente no sé como hacerlo.

Lo único que se me había ocurrido, es crear una clase derivada de SQLConnection, que durante su creación, pusiera el Active a False.

Saludos.

andres1569
03-07-2003, 23:01:17
Hola:

En primer lugar habría que crear una unit nueva y añadir este código:

unit DMBasic;

interface

uses
Classes, Forms, db, dbTables;

type
TBasicDataModule = class(TDataModule)
private
FDisconnectOnSave : Boolean;
protected
procedure WriteState(Writer: TWriter); override;
published
property DisconnectOnSave : Boolean read FDisconnectOnSave write FDisconnectOnSave;
end;

procedure Register;

implementation

uses dsgnintf;

procedure Register;
begin
RegisterCustomModule(TBasicDataModule, TCustomModule);
end;

{ TBasicDataModule }

procedure TBasicDataModule.WriteState(Writer: TWriter);
var
i : Integer;
begin
if FDisconnectOnSave then
begin
i := 0;
while i < ComponentCount do
begin
if Components[i] is TDataSet then
TDataSet(Components[i]).Active := FALSE
else if Components[i] is TCustomConnection then
TCustomConnection(Components[i]).Connected := FALSE

{ si usamos Delphi 4 o anterior, no hay TCustomConnection
else if Components[i] is TDatabase then
TDatabase(Components[i]).Connected := FALSE; }

// añadir aquí otros componentes desactivables

Inc(i);
end;
end;
inherited WriteState(Writer);
end;

end.

En esta unit declaramos la clase TBasicDataModule que hará de padre de nuestros DataModules. La propiedad DisconnectOnSave permite indicar si queremos que se guarden los componentes "conectables" con su propiedad Active / Connected a FALSE o si se dejan como están. El método WriteState se encarga de desactivar esos componentes antes de que se graben al .DFM.

El IDE de Delphi se mueve sólo con paquetes, por lo que para que aparezca la propiedad DisconnectOnSave en el Object Inspector y para que se ejecute realmente el método WriteState, tendremos que meterlo en un paquete, que puede ser de diseño y de ejecución a la vez. Compilando la unit sola, todo esto no funciona. Creamos el package, añadimos esta unit, compilamos e instalamos. Lo ideal es que tanto la unit como el package los guardemos en la carpeta \Lib de Delphi, si vamos a utilizarla en varios proyectos.

Modo de empleo:

1.- Abrimos nuestro proyecto y abrimos cualquier DataModule.
2.- Añadimos la unit DMBasic a la cláusula Uses.
3.- Cambiamos la declaración del DataModule:
TDataMain = class(TDataModule)
por
TDataMain = class(TBasicDataModule)
4.- Cerramos el DataModule, guardando los cambios.
5.- Aquí, en teoría bastaría con volverlo a abrir, pero, no sé porque, hay veces en que es necesario, o bien abrir el package que hemos creado antes y compilarlo de nuevo, o bien cerrar el project y volverlo a abrir. Hecho alguna de estas dos cosas, si todo ha ido bien, aparecerá la propiedad DisconnectOnSave en el Object Inspector. Si la ponemos a TRUE, y compilamos se encarga ya de cerrar todos los Datasets y Connections en el momento en que se escriba al .DFM.

Por cierto, parece excesivo dedicar todo un package a una sola unit, pero es posible que queramos añadir nuevas funcionalidades a este DataModule, con nuevas propiedades, eventos ... o incluso crear Forms a medida. Hasta hace bien poco no sabía que todo esto fuera posible aplicado a Forms y DataModules. La técnica está explicada en el siguiente truco de Ian Marteens:

http://www.marteens.com/trick16.htm

Bueno, no se si os gustará tener DataModules creados de esta forma, y dependientes de un package que debemos acompañar siempre, pero es una forma de solucionar esa pega que plantea Marc. Yo hasta el momento, lo que hago es desconectar el Database, y este a su vez cierra todos los Datasets que tenga enlazados, pero hay que acordarse de hacerlo.

guillotmarc
03-07-2003, 23:40:02
Impresionante.

Felicidades, es una opción muy buena. La verdad es que no se me había ocurrido, siempre he querido añadir propiedades y eventos en los formularios (para los formularios base), pero el hecho de ver que hacía falta crear un experto (que ni sé lo que es) para que aparecieran en el object inspector, me había hechado para atrás.

El hecho de depender de un package, no me molesta, ya tengo un par con componentes propios, que instalo siempre. Lo puedo incorporar allí, o añadir uno.

NOTA : En dbExpress no sirve el cerrar la conexión para cerrar los DataSets. Puesto que los datasets normalmente se tienen en 3 componentes : 1 Query + 1 DataSet Provider + 1 ClientDataSet. Cerrar la conexión, te cierra los querys, pero no los clientdatasets (los cuales al abrirse, ejecutan el query, abriendo la conexión). Por eso tendré que ampliar ligeramente tu código, para que también se cierren los clientdatasets.

Gracias de nuevo, un trabajo magnífico.

pedrohdez
04-07-2003, 09:44:43
Hola Marc,

Yo uso un sistema mas de andar por casa, pero me da el resultado apetecido, incluyo un par de linea en el fuente del proyecto (el .dpr) con lo siguiente


if Base.DB.Connected then
ShowMessage('LA BASE DE DATOS ESTA ABIERTA, ESTE EJECUTABLE ES DE USO INTERNO.');
Application.Run;


Es un poco "basto" pero me asegura que no envio el ejecutable a la calle con la BD abierta, a veces cambio el ShowMessage, que es un poco escandaloso, por cambiar el color de fondo de la aplicación a un amarillo fosforescente, que hace el mismo papel.

Saludos
Pedro.

guillotmarc
04-07-2003, 14:06:41
Hola.

El problema no es un tema de seguridad, sinó que la conexión de datos no tiene bien configurada la ruta de acceso a la base de datos. Eso es algo que tengo que especificar dentro del programa, localizando la base de datos manualmente (hay que buscarla, cada cliente la puede haber movido vete a saber donde).

Al no estar bien configurada la ruta de la conexión, si se abre automaticamente, dará un error y se interrumpirá el proceso de carga de la aplicación.

Saludos.

pedrohdez
04-07-2003, 15:28:25
Hola Marc,

Con el aviso anterior no me referia a nada de seguridad, si no a que cuando compilas tu aplicacion en tu propia maquina, no tienes pegas por que los enlaces los tendras apuntados a ficheros existentes, y no notas la diferencia entre dejarlos abiertos o no, mientras que con el aviso que proponia, te va a quedar claro que ese ejecutables no lo debes distribuir.

saludos,

guillotmarc
05-07-2003, 15:01:19
Hola.

No lo había entendido. Está bien, una vez compilado, cuando lo ejecutas, si te sale el mensaje es porqué has olvidado cerrar los datasets. Realmente es sencillo pero eficaz (las dos palabras que más me gustan, sobre todo sencillo :) :D ).

Gracias.