Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > Conexión con bases de datos
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Conexión con bases de datos

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 07-01-2009
Bauhaus1975 Bauhaus1975 is offline
Miembro
 
Registrado: may 2005
Ubicación: Málaga
Posts: 135
Poder: 20
Bauhaus1975 Va por buen camino
Unhappy TClientDataSet: No deja hacer '.append' sin query previa

Hola a todos.

Aquí estoy con un nuevo problema:
Resulta que para cargar los datos de un cliente en un formulario tengo un TClientDataSet conectado a un Provider y éste a la BBDD. Si intento añadir un nuevo cliente ('con cdataset.append') da error de que 'falta dataset' (parece que al provider).

El provider lo uso para otros suministros de datos relacionados con esa entidad (clientes), y resulta que si antes de añadir ese nuevo registro ha realizado una consulta lo añade sin problema. Parece que si el provider no tiene asociado un DataSet, no deja añadir un registro con la sentencia 'cdataset.append'. He probado a asignarle una TIBQuery vacía, pero al hacer el 'cdateset.open' previo al append da error por no tener definido SQL dicha TIBQuery.

¿Alguna idea?
Gracias y un saludo.
Responder Con Cita
  #2  
Antiguo 07-01-2009
Avatar de Caro
*Caro* Caro is offline
Moderadora
 
Registrado: jul 2004
Ubicación: Cochabamba, Bolivia
Posts: 2.544
Poder: 22
Caro Va por buen camino
Hola, eso es normal, necesitas un DataSet el cual esta relacionado con alguna de tus tablas para poder añadir registros, los cuales si los añades desde tu ClientdataSet se guardan en memoria hasta que tu hagas un ApplyUpdates que es cuando se guardan en tu tabla, tu provider es el que se relacionada con tu DataSet. Ahora si quieres hacerlo directamente con el ClientDatSet, igual tienes que guardar en algun lugar los datos que introduces ya sea un archivo XML o un binario.

Saluditos
__________________
Disfruten cada minuto de su vida a lado de sus seres queridos como si fuese el ultimo, uno nunca sabe lo que puede pasar.
Responder Con Cita
  #3  
Antiguo 07-01-2009
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Poder: 30
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
¡Hola!

Creo entender el problema, aunque ayudaría mucho que nos dijeras cuál es el mensaje de error exacto y nos detallaras un poco más cómo empleas los componentes y después de qué instrucción del programa se genera el error.

Verifica que la propiedad ResolveToDataSet esté en False y utiliza el evento OnGetTableName para indicarle el nombre de la tabla donde debe hacer el Insert Into.

Código Delphi [-]
procedure TdmProveedor.prClientesGetTableName(Sender: TObject;
  DataSet: TDataSet; var TableName: String);
begin
  inherited;
  TableName := 'CLIENTES';
end;

Espero te sirva, nos comentas cómo te fue.

Al González.

P.D. Ahora veo el comentario de Linett, y, según noto en Provider.pas, es posible que no baste usar el evento que señalé. El problema que veo es que primero se intenta obtener el nombre de la tabla a través del conjunto de datos que tenga asociado, y luego mediante el evento. Ese orden no está del todo mal, el verdadero problema es que se asume que habrá siempre un conjunto de datos asociado (Tree.Source).
Código Delphi [-]
  if TableName = '' then
    TableName := IProviderSupport(Tree.Source).PSGetTableName;
  Provider.DoGetTableName(Tree.Source, TableName);
(encontrado con Find in Files en la unidad Provider.pas de Delphi 7)

Última edición por Al González fecha: 07-01-2009 a las 19:22:20.
Responder Con Cita
  #4  
Antiguo 07-01-2009
Bauhaus1975 Bauhaus1975 is offline
Miembro
 
Registrado: may 2005
Ubicación: Málaga
Posts: 135
Poder: 20
Bauhaus1975 Va por buen camino
Hola, gracias por tu ayuda.
Hmmm, entonces... pueden ser soluciones:

- Antes de hacer un .append para el nuevo registro, ¿hacer una query 'nula' (WHERE ID=0 o similar) aunque no devuelva ningún resultado y vincularla al provider?

Realmente no es que quiera trabajar directamente con el ClientDataSet, para editar sí que tengo datos que una TIBQuery conecta al provider (en t.ejecución), pero para el caso del alta no sé me ocurre otra cosa. A lo mejor hay otras propiedades o posibilidades que no conozco. Y lo de los ficheros, no creo que me venga bien para este caso.

Todos estos problemillas se me dan porque no tengo experiencia, Además de la cuestión de esta pregunta, cuando comencé el proyecto no sabía como organizar mejor la serie de componentes de base de datos, pero bueno, esto es para crear una discusión en un nuevo post.
Saludos.
Responder Con Cita
  #5  
Antiguo 07-01-2009
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Poder: 30
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Para aclarar las cosas, supongo que el error ocurre cuando haces una llamada al método ApplyUpdates (no al llamar a Append).

¿Sin tener asignado un conjunto de datos (data set) al proveedor, te funcionó el evento? Por lo que vi en el código de Provider.pas, parece que tu respuesta (que aún espero) será no.

En ese caso la solución que propones de una consulta sin resultados es buena (mientras ésta contenga todas las columnas que son campos reales editables en el ClientDataSet), pero no me queda claro por qué no usas la misma consulta que empleas para cargar el listado de clientes.

Saludos.

Al.
Responder Con Cita
  #6  
Antiguo 07-01-2009
Bauhaus1975 Bauhaus1975 is offline
Miembro
 
Registrado: may 2005
Ubicación: Málaga
Posts: 135
Poder: 20
Bauhaus1975 Va por buen camino
Buenas Al González, vamos allá, a ver si puedo darte bien toda la información. Ocurre que respondí a Caro justo después de tu primera respuesta, así que era su respuesta. Ahora vamos con tus preguntas:

Cita:
Empezado por Al González Ver Mensaje
Creo entender el problema, aunque ayudaría mucho que nos dijeras cuál es el mensaje de error exacto
Efectivamente, el/los errores los da en la sentencia 'Open':
Código Delphi [-]
ClientDataSet.Close;
ClientDataSet.Open;
ClientDataSet.Append;

Tenemos dos errores, según las dos cosas que he probado:
1. Al intentar dar de alta un cliente, sin que el provider haya realizado ninguna query antes: 'Missing DataSet property' (al realizar el Open)
2. Tras añadir una query 'vacía' en t.ejecución, en la misma sentencia lanza el error 'empty SQL statament'. (es evidente, al estar la query vacía)

Cita:
Empezado por Al González Ver Mensaje
Verifica que la propiedad ResolveToDataSet esté en False y utiliza el evento OnGetTableName
para indicarle el nombre de la tabla donde debe hacer el Insert Into.
He probado, y no ha dado resultado. Todo ha seguido igual (ResolveToDataSet ya estaba en False).

Aún no he probado la consulta para id=0 o similar que nos deje una estructura de campos en el DataSet, ¿lo dejaremos mejor como última solución al ser un poco parche?

Cita:
Empezado por Al González Ver Mensaje
por qué no usas la misma consulta que empleas para cargar el listado de clientes.
Buena apreciación, pero en algunos casos tengo accesos directos a crear cliente en el programa, y no ha existido ninguna consulta relacionada con ese provider.
Seguiremos pensando...
Responder Con Cita
  #7  
Antiguo 07-01-2009
Bauhaus1975 Bauhaus1975 is offline
Miembro
 
Registrado: may 2005
Ubicación: Málaga
Posts: 135
Poder: 20
Bauhaus1975 Va por buen camino
Bueno, ahora intentaré explicaros un poco como me he organizado con los componentes y Units. El caso en cuestión es para gestionar 'clientes' (de manera típica), y como el funcionamiento que había pensado es común dentro del programa para más cosas quería exportarlo a más entidades.

Unidades:
-formListadoClientes: Listado de clientes, búsquedas.
-formFichaCliente: Datos del cliente, podemos llegar desde listado, para editar o crear, o desde otras partes del programa directamente.
-ClassCliente: Una unidad donde implemento la clase con funcionalidad de consultas y todo lo referente a clientes. Aquí formo el SQl para cada caso en los métodos de clase, y para hacer las consultas asigno un TIBQuery (es una propiedad de la clase) al provider en t.ejecución.
-DMGeneral: Conexión de BBDD y una transacción, y algunas consultas globales para el programa.
-DMLogicalData: Uso un provider para cada entidad, un clientdataset para los listados, y otro para la ficha.
Como veis, explicitamente no hay TIBquery en los DataModules,

Quizá quiera tener todo demasiado organizado y me equivoque en algunas cosas, pero mi idea era buscar un patrón bueno de funcionamiento para estos tipos de entidades 'cliente', 'articulo', 'expediente' que suelen funcionar igual (listados y fichas). Crear una clase padre y de ella heredar los casos particulares. No sé cómo de bien o mal voy haciendo las cosas...

Si conoceis alguna lectura buena de como organizar mejor la serie de componentes para comunicación con base de datos también os estaría muy agradecido.

Gracias por las ayudas que me vais dando
Responder Con Cita
  #8  
Antiguo 07-01-2009
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Poder: 30
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Gracias por las aclaraciones.

Entonces la excepción se eleva cuando se intenta abrir el conjunto de datos. Es al hacer el Open y no el Append cuando ocurre el error (aunque probablemente se queda seleccionada la línea del Append, eso es algo normal del depurador).

En lugar de Open podrías intentar con el método CreateDataSet (teniendo ya preparados los campos el conjunto de datos cliente, lo cual seguramente ya haces / está, puesto que de otra manera tampoco el Open funcionaría).

Pero el problema entonces será cuando intentes hacer el obligado ApplyUpdates para enviar el nuevo registro a la base de datos. Es ahí cuando el proveedor demandará la existencia de un conjunto de datos asociado. Esto podría parecer algo ocioso por parte de un TDataSetProvider cuya propiedad ResolveToDataSet es False, pero resulta necesario porque es a través de propiedad DataSet como finalmente logra el acceso de escritura a la base de datos. Ciertamente no hace nada sobre ese conjunto de datos, pero sí lo usa para obtener la vía (conexión) hacia la base de datos. Al menos así es en Delphi 7.

Entonces, quedarían dos principales alternativas:
1. El truco que mencionas de que el proveedor tenga asociada una consulta "Select ... Where ID = 0".
2. Crear un derivado de TDataSetProvider para redefinir su método virtual InternalApplyUpdates, y con ello el comportamiento que el componente tiene bajo estas circunstancias.
3. Lanzar la propia instrucción "Insert Into" desde el conjunto de datos cliente, colocando ésta en la propiedad CommandText y llamando luego a su método Execute. Pero para esto también es necesario que el proveedor tenga valor en su propiedad DataSet, aunque en este caso sí puede estar vacío el query.

Para salir rápido del problema, yo haría lo primero. Pero si quisiera construir una solución que vea como algo útil para muchos casos similares actuales o futuros, probablemente estudiaría la segunda opción (considerando el tiempo que conllevaría crear un componente derivado para estos fines). La tercera opción me parece la menos adecuada.

Mucho de esto que te comento tiene que ver con la manera en que los componentes de acceso a datos manejan e implementan la interfaz IProviderSupport. Síguele la pista a dicha interfaz y descubrirás varios asuntos interesantes relacionados con el manejo de objetos TClientDataSet.

Espero haber orientado un poco más, no dejes de emitir tus opiniones y resultados.

Saludos.

Al.

P.D. Acabo de ver que publicaste un comentario más, más tarde le echaré un vistazo...

Última edición por Al González fecha: 07-01-2009 a las 22:08:47.
Responder Con Cita
  #9  
Antiguo 07-01-2009
Bauhaus1975 Bauhaus1975 is offline
Miembro
 
Registrado: may 2005
Ubicación: Málaga
Posts: 135
Poder: 20
Bauhaus1975 Va por buen camino
Qué gran explicación, muchas gracias por tu tiempo.

Cita:
Empezado por Al González Ver Mensaje
En lugar de Open podrías intentar con el método CreateDataSet (teniendo ya preparados los campos el conjunto de datos cliente, lo cual seguramente ya haces / está, puesto que de otra manera tampoco el Open funcionaría).
He probado también a definir los Fields, con el FieldsEditor en el ClientDataSet (sin resultado positivo), pero no he probado a introducir la sentencia CreateDataSet.

Sobre el punto 2. que comentas no se realmente que tendría que conseguir implementar. Mañana seguiré haciendo pruebas con estos últimos aportes.
Un saludo.
Responder Con Cita
  #10  
Antiguo 11-01-2009
Bauhaus1975 Bauhaus1975 is offline
Miembro
 
Registrado: may 2005
Ubicación: Málaga
Posts: 135
Poder: 20
Bauhaus1975 Va por buen camino
Buenas de nuevo.
He probado con la 'query vacia' (SELECT * FROM cliente WHERE ID=0) que hemos hablado para que el provider tenga un DataSet inicial y así poder referirse a la tabla apropiada.
Resulta que la ejecución del cds.Open lanza la excepción EIBInterbaseError:'No current record for fetch operation'. Parece que el problema es que no hay ningún registro devuelto, no sabía que eso produjera una excepción. ¿Qué es mejor hacer? ¿Capturar la excepción e ignorarla?
Responder Con Cita
  #11  
Antiguo 11-01-2009
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Poder: 30
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Te sugiero probar esa consulta directamente desde IB Expert (o el programa de administración que uses) para averiguar si realmente es por ese intento de apertura.
Responder Con Cita
  #12  
Antiguo 12-01-2009
Bauhaus1975 Bauhaus1975 is offline
Miembro
 
Registrado: may 2005
Ubicación: Málaga
Posts: 135
Poder: 20
Bauhaus1975 Va por buen camino
Hola. Me alegro de leer de nuevo tus consejos.
He estado haciendo pruebas en base a lo que me has dicho. He usado para ello la herramienta IBExpert para hacer las consultas, y efectivamente, el error viene desde Firebird (2.1)

Este el código SQL de la consulta. La entidad en cuestión se llama contacto, aunque yo todo el rato la he llamado 'clientes' para que me entendais mejor.

Código SQL [-]
SELECT c.ID as IDContacto,c.NIF,c.Nombre,c.Apellidos,t.valor as TipoContacto
FROM contacto c, tipo t
LEFT JOIN cliente cl on c.IDCliente = cl.ID
LEFT JOIN empresa e on c.IDEmpresa = e.ID
WHERE CONDICION AND c.IDTipoContacto = t.ID

Este es un resumen del estudio, el cual no entiendo por que se comporta así.
Siendo 'CONDICION' esta combinacion de casos y resultados:

C.ID > 0 => Devuelve la lista de registros existentes correctamente.
C.ID = null => 'The cursor identified in the update or delete statement is not positioned on a row'
C.ID = 0 => IDEM al anteior caso.
IDContacto > 0 => devuelve registro vacio con campos (increible)
IDContacto = null => devuelve registro vacio con campos
IDContacto = 0 => devuelve registro vacio con campos
(como 'C.IDContacto' Da error de que no existe tal columna)

Por lo que finalmente lo he resuelto formando la sentencia SQL dinamicamente de esta forma:
Código Delphi [-]
if (id= 0) then
   SQL := 'WHERE IDContacto = null '
else
   SQL := 'WHERE c.ID = '+IntToStr(id);

Y ahora parece que funciona, tanto el caso 'normal' donde el DataSet estaba ya creado, como en el caso de usar este query nula para los casos donde nunca se habia realizado consulta (accesos directos a crear Contacto y demas)

Aunque realmente no entiendo nada de por que este comportamiento, supongo que seran cosas del motor de BBDD ¿cierto?

Muchas gracias de nuevo por tu ayuda y un saludo.
Responder Con Cita
Respuesta



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

Temas Similares
Tema Autor Foro Respuestas Último mensaje
Como sabe un query a que tabla quiero hacer append? coso Conexión con bases de datos 4 14-06-2008 20:26:11
Porque lee la tabla completa al hacer append? judit25 Conexión con bases de datos 2 31-05-2006 16:20:37
No me deja hacer la integridad referencial en paradox Sayuri Conexión con bases de datos 6 27-07-2005 22:53:03
Error al hacer un Append Ivr Conexión con bases de datos 0 22-02-2005 11:55:19
Como hacer una Vista Previa? Gabriel2 Impresión 3 22-12-2004 15:15:20


La franja horaria es GMT +2. Ahora son las 15:59:14.


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