PDA

Ver la Versión Completa : Método retorna Set de datos


gustavosv
29-02-2012, 22:50:07
Hola, hace unos días preguntaba en éste foro sobre el tema de 'Abstraccción', y las respuestas que generosamente me bridaron implican un estudio juicioso del tema, y bueno, como no solo del amor vive el hombre (hay que generar dinero), decidí tomar un camino intermedio mientras tanto.

La historia es que estoy trabajando en un proyecto de bases de datos que de antemano quiero que me sirva tanto para una empresa pequeña que solo quiera usar una BD como MS Access o para otra más grande que ya se le mida a temas como MySQL, entonces pensé en 3 capas, front al usuario (concepto visual) por un lado, unas clases que me representen las tablas de la BD y todas las acciones que se necesiten sobre ellas (segunda capa) y otras clases que encapsulen la conexión al servidor , las consultas SELECT y los CRUDs (tercera capa), pensando que en ésta última sea la única a la que tenga que 'meterle mano' al cambiar de una BD a otra.

En éste punto me enredé y lancé un hilo "Abstracción en BD" y quienes escribieron en ese post hablaron de Persistencia, ORM y enlaces muy enriquecedor que pienso leer, pero como decía, el tiempo me acosa, debo hacer entregas y tengo fecha límite, por lo que tomé un camino intermedio, decidí hacer solo 2 capas, visual y clases de tablas de la BD con sus funciones y métodos que tienen dentro de ellas las SQL usando ADO.

Ahora el asunto es que espero que el método 'BuscarListaClientes' de la clase TCliente me retorne un conjunto o set de datos, p.ej. todos los clientes de una ciudad, y no he logrado conseguirlo, he intentado que el método 'BuscarListaClientes' devuelva un DataSource, un ClientDataSet y nada me funciona. El código que anexo es para llenar un DBGrid con la lista de Agentes Comerciales de la empresa:

En la forma Fpedidos:
procedure TFPedidos.Button1Click(Sender: TObject);
var
vdatos: TDataSource;
begin
vdatos := TDataSource.Create(nil);
vdatos := oAgenteCial.Leer(FPrincipal.oConexion.GetConexion);
DBGrid1.DataSource := vdatos;
end;

En la clase:

function TAgenteCial.Leer(AConexion: TADOConnection): TDataSource;
var
vCommandText: string;
vDatos: TADOQuery;
begin
Result := nil;
vDatos := TADOQuery.Create(nil);
vDataSource := TDataSource.Create(vDatos);
vDatos.Connection := AConexion;
vCommandText := 'SELECT venId, venNombre FROM GEN_Vendedores ';

vDatos.SQL.Text := vCommandText;
try
vDatos.Active := True;
if not vDatos.Eof then
Result := vDatos.DataSource;
finally
FreeAndNil(vDatos);
end;
end;



El problema es que no recibo el set de datos, y como decía he intentado con varios componentes y marca exception class $C0000005 access violation at 0x09-...


Alguien ha resuelto algo similar ?

roman
01-03-2012, 00:07:15
Pero tal como lo esbozas no parece tener muchas posibilidades.

De entrada, el método Leer devuelve vDatos.DataSource pero a esta propiedad del objeto ADOQuery nunca le asignas nada, además de que dicha ppropiedad es para el paso de parámetros al Query. Quizá quisiste devolver el DataSource que creaste, pero, por un lado, tal objeto creado no apunta, aparentemente, a ningún DataSet y tampoco está declarado en el método. ¿De dónde sale?

Por otro lado, si lo que quieres es devolver el DataSet que creas al vuelo, ya sea directamente o através de un DataSource, entonces no puedes destruir el objeto en el mismo método, porque entonces la rutina que lo reciba obtendrá un objeto muerto.

Tendrías que dejar que sea el receptor quien se encargue de destruir al DataSet, lo cual es propenso a errores pues le dejas una responsabilidad que probablemente no deba tener.

Alguna vez hice algo parecido, basándome en código de otros, en donde lo que devuelves, no es un DataSet sino una interfaz que contiene una DataSet. Dicha interfaz estaba implementada por un descendiente de TInterfacedObject y lo que logras con eso es un recolector de basura. Esto es, el objeto se destruye automáticamente una vez que se pierden todas las referencias a él (es la idea, los detalles no los recuerdo bien ahora).

De todas formas, no es algo tampoco que vayas a hacer en un rato.

No sé yo si estás tomando el camino adecuado dada la premura de tiempo.

Tú puedes hacer aplicaciones con varias capas usando una triada


DataSet <---> DataSetProvider <---> ClientDataSet


exponiendo únicamente el ClientDataSet al cliente. Esto te permite cambiar con relativa facilidad la capa del DataSet. En una aplicación real a tres capas, el DataSet viviría en un RemoteDataModule, pero por lo mientras lo puedes colocar en un DataModule normal. Creo que alguien (¿Al González?) llamó a esto una aplicación a dos capas y media :)

// Saludos

gustavosv
01-03-2012, 00:47:19
Hola Roman, la triada DataSet <---> DataSetProvider <---> ClientDataSet la he usado con DM locales como dices, quería 'avanzar' a algo más OOP, por ello la idea en la que me embarqué, tienes razón en cuanto a que al método Leer le sobra vDataSource := TDataSource.Create(vDatos);, he hecho varias pruebas y se me quedó pegado.

En medio de las pruebas, llegué a la conclusión de la destrucción del objeto, pero no entendía porqué ...
Por otro lado, si lo que quieres es devolver el DataSet que creas al vuelo, ya sea directamente o através de un DataSource, entonces no puedes destruir el objeto en el mismo método, porque entonces la rutina que lo reciba obtendrá un objeto muerto.

Para mantener esa idea de trabajo, qué puedo devolver, que me permita en 'ese lado del programa' no usar componentes de ADO ? Podría devolver un TClientDataSet ?

Qué me recomiendas ...?

gustavosv
01-03-2012, 01:25:06
mira, llegué a la solución que muestro ...

en la forma FPedidos:

procedure TFPedidos.Button1Click(Sender: TObject);
var
vSource: TDataSource;
begin
vSource := TDataSource.Create(nil);
vSource := oAgenteCial.Leer(FPrincipal.oConexion.GetConexion);
if not (vSource = nil) then
DBGrid1.DataSource := vSource;
end;

en la clase ...

function TAgenteCial.Leer(AConexion: TADOConnection): TDataSource;
var
vQuery: TADOQuery;
vSource: TDataSource;
begin
Result := nil;
vQuery := TADOQuery.Create(nil);
vSource := TDataSource.Create(nil);
vQuery.Connection := AConexion;

vQuery.SQL.Text := 'SELECT venId, venNombre, venXMLClienteCons FROM GEN_Vendedores ';
try
vQuery.Active := True;
if not vQuery.Eof then
begin
vSource.DataSet := vQuery;
Result := vSource;
end;
finally
// FreeAndNil(vQuery);
// FreeAndNil(vSource);
end;
end;

El problema es que como lo decías, el componente no hacía nada porque estaba mal asociado, estaba asociando el DataSource al Query y debía asociar el Query al DataSource: vSource.DataSet := vQuery;
El asunto es que queda el temita de la liberación de los objetos vQuery y vSource ...:confused:

roman
01-03-2012, 02:23:54
En todo caso, yo devolvería el DataSet en lugar del DataSource y así sólo tienes que preocuparte por la destrucción de un objeto.

De todas formas, habría que entender qué es eso de "algo más OOP". ¿A qué te refieres? Toda la VCL de delphi es OOP. Un DataSet, un DataSource, un DBEdit, un DBGrid, todos ellos son objetos.

Si ya has trabajado con la triada mencionada, ¿qué ganas proveyendo los mismos objetos, como un DataSet, mediante un método? ¿Dónde está la independencia de la capa de datos si métodos como Leer reciben un componente tan específico como un TADOConnection?

Quizá, y recalco el quizá, una idea sencilla como ésta (http://edn.embarcadero.com/article/28156) de Wayne Niddery, te pueda dar un punto de partida parra algo no tan complicado.

// Saludos

2-D@monic
21-08-2012, 20:33:23
Ahora me toca a mí..... cómo resolviste este problema?
Sabes analizando el caso se puede hacer esto con clientdataset, llenándolo para que esté disponible "off-line" y devolverlo, el problema es el manejo de memoria, es por esto que sale el access violation, yo intenté hacerlo así estoy usando C++ builder y la verdad manejar punteros no es mi fuerte.....
Por temas de tiempo tampoco pude hacerlo de la manera que tendría que ser para disminuir el acople de objetos (gracias! enfoque OOP) y lo hice como todos lo hacemos, si quieres profundizar en este enfoque de tres capas tienes que utilizar DataSnap, tómate tu tiempo pero verás que valió la pena.