Ir al contenido


Foto

WebService y TRemotable


  • Por favor identifícate para responder
11 respuestas en este tema

#1 apicito

apicito

    Advanced Member

  • Miembros
  • PipPipPip
  • 104 mensajes

Escrito 12 noviembre 2011 - 02:58

Estoy construyendo un servidor de un WebService siguiendo el tutorial Servicios Web Seguros en Delphi de DanySoft
Ya tengo la parte servidora. Básicamente el WS funciona de la siguiente forma: se le envia un array de TRemotables (osea de registros) y me devuelve el mismo array con algún dato cambiado. Concretamente en la propiedad "resultado".


delphi
  1.   TRexistro            = class;               
  2.   listaRexistros      = array of TRexistro;
  3.  
  4.   TRexistro = class(TRemotable)
  5.   private
  6.     Ftipo: string;
  7.     Fdocumento: string;
  8.     Fresposta: string;
  9.   published
  10.     property tipo:      string  read Ftipo write Ftipo;
  11.     property documento: string  read Fdocumento write Fdocumento;
  12.     property resposta:  string  read Fresposta write Fresposta;
  13.   end;


He creado un cliente desde el que importé el WSDL del servidor y que funciona correctamente con datos distintos al TRemotable, es decir, si envio un string y me devuelve lo mismo.
Pero cuando trato de creal el TRemotable para enviarlo me da un "access violation" que por más que he probado alternativas no he conseguido evitar. Este es el código:


delphi
  1. procedure TForm4.Button2Click(Sender: TObject);
  2. var
  3.   lista    : listaRexistros;
  4. begin
  5.  
  6.   //lista := listaRexistros.create();
  7.   setlength(lista,3);
  8.  
  9.   //lista[0] := TRexistro.Create;
  10.   lista[0].tipo := 'D';
  11.   lista[0].documento := '11111111A';
  12.   lista[0].resposta := 'X';
  13.  
  14.   //lista[1] := TRexistro.Create;
  15.   lista[1].tipo := 'A';
  16.   lista[1].documento := '22222222A';
  17.   lista[1].resposta := 'X';
  18.  
  19.   //lista[2] := TRexistro.Create;
  20.   lista[2].tipo := 'T';
  21.   lista[2].documento := '33333333A';
  22.   lista[2].resposta := 'X';


El error me lo da cuando intento asignarle un valor string:
lista[0].tipo := 'D';
Ya he probado con las líneas que están comentadas y sin ellas.
Alguien me puede ayudar?.

  • 0

#2 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 12 noviembre 2011 - 09:34

Hola

Ésto debe funcionar,



delphi
  1. procedure TForm4.Button2Click(Sender: TObject);
  2. var
  3.   lista    : listaRexistros;
  4. begin
  5.  
  6.   setlength(lista,3);
  7.  
  8.   lista[0] := TRexistro.Create;
  9.   lista[0].tipo := 'D';
  10.   lista[0].documento := '11111111A';
  11.   lista[0].resposta := 'X';
  12.  
  13.   lista[1] := TRexistro.Create;
  14.   lista[1].tipo := 'A';
  15.   lista[1].documento := '22222222A';
  16.   lista[1].resposta := 'X';
  17.  
  18.   lista[2] := TRexistro.Create;
  19.   lista[2].tipo := 'T';
  20.   lista[2].documento := '33333333A';
  21.   lista[2].resposta := 'X';
  22. end;



Yo he probado ésto y funciona sin problema.



delphi
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. var
  3.   Consultas: ConsolidacionWS;
  4.   Lista: ListaRegistros;
  5.   i: Integer;
  6.  
  7. begin
  8.  
  9.   SetLength(Lista,3);
  10.  
  11.   Lista[0] := Registro.Create;
  12.   Lista[0].asunto := 'hola;';
  13.   Lista[0].estado := 1;
  14.  
  15.   Consultas := GetConsolidacionWS(true,'',nil);
  16.  
  17. end;



SaludOS
  • 0

#3 apicito

apicito

    Advanced Member

  • Miembros
  • PipPipPip
  • 104 mensajes

Escrito 12 noviembre 2011 - 09:47

E r e s  u n  C r a c k !!!
Debe de ser la única combinación que no había probado. LLevaba toda la mañana dandole vueltas a esto... buscando en San Google... y en un minuto la ciencia llega a mí.
Te nombre mi especialista de cabecera en temas de TRemotables... recuerdas que hace nos días ya me ayudaste con un tema muy parecido?
Como siempre gracias!!!.

  • 0

#4 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 12 noviembre 2011 - 10:03

Hola apicito

Si, recuerdo tu problema que tenia que ver precisamente con arreglos de clases TRemotable.

Pues que bueno que te haya servido :)

SaludOS

PD: Gracias por lo de crack pero lejos estoy de serlo, sólo que cuando uno habla de lo que sabe siempre parece mas inteligente que los demás :D :D :D
  • 0

#5 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 12 noviembre 2011 - 01:17

El error es bastante simple de explicar. ¡Simplemente no estabas creando el objeto!


El haber reservado memoria para el array, no implica que se reserve la memoria para cada objeto dentro de éste.
Recuerda que OBLIGATORIAMENTE para hacer uso de una instancia, primeramente debe estar creada; a menos que se invoque a algún método de clase.


Como consejo, considera utilizar TObjectList o algún otro xxxList en lugar de arrays dinámicos.  ;)


Saludos,
  • 0

#6 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 12 noviembre 2011 - 08:08

El error es bastante simple de explicar. ¡Simplemente no estabas creando el objeto!


El haber reservado memoria para el array, no implica que se reserve la memoria para cada objeto dentro de éste.
Recuerda que OBLIGATORIAMENTE para hacer uso de una instancia, primeramente debe estar creada; a menos que se invoque a algún método de clase.


Como consejo, considera utilizar TObjectList o algún otro xxxList en lugar de arrays dinámicos. ;)


Saludos,


Así es, es un asunto de creación de objetos y es un asunto elemental, por eso he dicho que lo de crack queda muy alto :D.

El detalle es que el array viene desde un webservice y es necesario utilizar ese tipo de Arreglo y puede ser que no se conoce la cantidad de elementos que contiene :)

SaludOS
  • 0

#7 apicito

apicito

    Advanced Member

  • Miembros
  • PipPipPip
  • 104 mensajes

Escrito 13 noviembre 2011 - 02:07

Egostar, no me queda claro si dices que si se puede utilizar el objetList no no.


  • 0

#8 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 13 noviembre 2011 - 09:02


El detalle es que el array viene desde un webservice y es necesario utilizar ese tipo de Arreglo y puede ser que no se conoce la cantidad de elementos que contiene

SaludOS

Yo de web services no tengo ni la más remota idea por lo que mucho no sabría decir al respecto. Si es que en verdad resulta ser que desde la(s) clase(s) base(s) el comportamiento está hecho en base a arrays puedo entenderlo. Pero de lo que veo de código no logro entender el porqué no se podría adaptar el código para trabajar con TObjectList.


Y déjame decirte que aunque nos viéramos obligados a trabajar internamente con arrays de la vieja escuela hay formas de pasar a un esquema basado en TObjectList. Esto es posible gracias al patrón Adapter. Este patrón sugiere basicamente ofrecer una interfaz adecuada a nuestros intereses y ocultar los detalles de la interfaz incompatible. Hay dos maneras de encararlo: Adaptador por Objeto, o Adaptador de Clase. En el primero el Adaptador es una clase con los métodos y una interfaz bien diseñada que mantiene como atributo la clase base. El Adaptador de Clase consiste en heredar de la clase y ofrecer los métodos adecuados, y de ser necesario sobreescribirlos. En la zona de descargas hace tiempo dejé ejemplos sencillos de este patrón.  ;)  Hago saber que existe una ligera variación al Adaptador de Objeto a la que Zarko llama Adaptador por polimorfismo, no confundirlo como si se tratase de una 3ra forma, es sólo una variación.


Saludos,
  • 0

#9 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 13 noviembre 2011 - 03:57



El detalle es que el array viene desde un webservice y es necesario utilizar ese tipo de Arreglo y puede ser que no se conoce la cantidad de elementos que contiene

SaludOS

Yo de web services no tengo ni la más remota idea por lo que mucho no sabría decir al respecto. Si es que en verdad resulta ser que desde la(s) clase(s) base(s) el comportamiento está hecho en base a arrays puedo entenderlo. Pero de lo que veo de código no logro entender el porqué no se podría adaptar el código para trabajar con TObjectList.


Y déjame decirte que aunque nos viéramos obligados a trabajar internamente con arrays de la vieja escuela hay formas de pasar a un esquema basado en TObjectList. Esto es posible gracias al patrón Adapter. Este patrón sugiere basicamente ofrecer una interfaz adecuada a nuestros intereses y ocultar los detalles de la interfaz incompatible. Hay dos maneras de encararlo: Adaptador por Objeto, o Adaptador de Clase. En el primero el Adaptador es una clase con los métodos y una interfaz bien diseñada que mantiene como atributo la clase base. El Adaptador de Clase consiste en heredar de la clase y ofrecer los métodos adecuados, y de ser necesario sobreescribirlos. En la zona de descargas hace tiempo dejé ejemplos sencillos de este patrón.    Hago saber que existe una ligera variación al Adaptador de Objeto a la que Zarko llama Adaptador por polimorfismo, no confundirlo como si se tratase de una 3ra forma, es sólo una variación.


Saludos,


Hola amigo,

Vamos a ver, a través del WSDL Importer delphi recupera la estructura de clases de un WebService, por ejemplo, ésto es una porción de lo que se recupera.

[Delphi]

TRexistro            = class;               
  listaRexistros      = array of TRexistro;

  TRexistro = class(TRemotable)
  private
    Ftipo: string;
    Fdocumento: string;
    Fresposta: string;
  published
    property tipo:      string  read Ftipo write Ftipo;
    property documento: string  read Fdocumento write Fdocumento;
    property resposta:  string  read Fresposta write Fresposta;
  end;

[/Delphi]

Suponiendo que no tenemos acceso al código del webservice y la definición de la clase TRexistro y el arreglo listaRexistros ya está definida como se muestra, en base a tu explicación ¿como se puede hacer esa adaptación que refieres?.

Me interesa mucho saberlo ya que tengo algunos detalles con un webservice con clases que tienen ma clases dentro de su definición, algunos hasta con 3 o 4 niveles  y algunos castings de clases genericas para ser usadas por diferentes definiciones, si puedo simplificar eso seria genial :)

Salud OS

  • 0

#10 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 13 noviembre 2011 - 09:06

Hola,


Para poder llevar a cabo el patrón Adapter es necesario contar con una visión de la clase cliente. Para el caso, se necesita saber la clase cliente de esa lista de TRegistro. Y quizá, examinar la estructura de la clase TRemotable para ver en que y como es que afectaría al cliente.


Si no existe, o en todo caso si el importador no lo crea, entonces pasa por el desarrollador diseñar la clase cliente. En este panorama directamente no hace falta patrón alguno... directamente se diseña a la clase para que administre el arreglo y se encargue de ofrecer una interfaz adecuada para sus intereses.


Si me puedes indicar si además de esa clase y la lista hay otras sería lo más adecuado antes de seguir analizando y determinar si es válido y conveniente un Adapter.
Por lo pronto haré de cuenta de que no existe tal clase y ofreceré un ejemplo sencillo a vuelo:




delphi
  1. TConsumerListaRegistro = class
  2. private
  3.   FListaRegistros: TListaRegistro;
  4.   // métodos get y set para acceder a la lista de registro
  5.   function GetRegistro(Index: integer): TRegistro;
  6.   procedure SetRegistro(Index: integer; Value: TRegistro);
  7. public
  8.   function Counts: integer;
  9.   procedure SetLengthList(Value: integer);
  10.   // SetLenghList asigña tamaño a la lista
  11.   function  CreateRegistro: TRegistro; overload;
  12.   function CreateRegistro(Index: integer): TRegistro; overload;
  13.   // nos crea un objeto de la clase TRegistro
  14.   procedure FreeRegistro(Index: integer); overload;
  15.   procedure FreeRegistro(Registro: TRegistro); overload;
  16.   // libera un registro de la lista. OJO: liberar no alterar el tamaño de la lista
  17.   function AddRegistro(Registro: TRegistro): integer;
  18.   // Agrega el registro a la lista, altera el tamaño
  19.   function DeleteRegistro(Registro: TRegistro): integer; overload;
  20.   procedure DeleteRegistro(Index: TRegistro); overload;
  21.   // Elimina el registro de la lista, altera el tamaño
  22.   property Registros[I: integer]: TRegistro read GetRegistro write SetRegistro;
  23.   // propiedad vectorial de acceso a la lista
  24. end;




Como pueden apreciar la clase asume el trabajo de crear, liberar, etc. la lista de objetos de clase TRegistro. Por debajo de esto esconde todo un trabajo con el array y operaciones con vectores de toda la vida... Creería que el código podría resultarles elemental y se hacen una idea. En su lugar ofrece una interfaz basada en la propiedad vectorial Registros[] gracias a los métodos GetRegistro y SetRegistro.


Una posible implementación de dichos Getter y Setter puede ser algo como:




delphi
  1. function TConsumerListaRegistro.GetRegistro(Index: integer): TRegistro
  2. begin
  3.   if (Index >= Low(FListaRegisto)) AND (Index <= High(FListaRegistro))
  4.       then result := FListaRegistro[Index];
  5. end;
  6.  
  7.  
  8. procedure TConsumerListaRegistro.SetRegistro(Index: integer; Value: TRegistro);
  9. begin
  10.   if (Index >= Low(FListaRegisto)) AND (Index <= High(FListaRegistro))
  11.       then FListaRegistro[Index] := Value;
  12. end;




Ahora ya no hay que preocuparse por el array. La clase nos permite hacer cosas como éstas:




delphi
  1. var Reg5: TRegistro;
  2. Reg5 := MiConsumer.Registros[5];




Incluso se puede iterar:




delphi
  1. var j: integer;
  2. begin
  3. for j := 0 to MiConsumer.Counts -1 do
  4.   MiConsumer.Registros[j].Documento := 'Documento' + IntToStr(j);
  5. ...




Creo que con esto se entiende la idea 


Sobre lo que me comentas de que tienes algunos de anidamiento de clases, ese enfoque es quizá más complejo y habría que analizar mejor el caso. Tendría que tener conocimiento de qué, y como es que se lleva a cabo. Podría implementarse, quizá, con una variación del patrón Composite y Adapter, como éste.... un AdapterComposite (que no es algo raro de encontrar) si la idea es tratar a ese anidamiento de clases como si se tratase de una clase simple, y mientras todos tuvieran alguna interfaz en común. De no ser así el escenario sugeriría otro enfoque... pero para ello se requiere de un mejor estudio.


Lo más directo, simple y elemental sería propagar mi idea:




delphi
  1. miConsumer.Registros[1].SubRegistros[2].SubSubRegistros[3].Documento := 'hola';




Pero esto, como puede verse conduce a confusiones si se propaga demasiado... ¡se están violando la Ley de Démeter, hay demasiado acoplamiento y anidamiento.


Si me das más datos...


Saludos,
  • 0

#11 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 14 noviembre 2011 - 07:27

Muy buen código Delphius, tanto para enfrentar el problema en cuestión,  así como base para muchos otros.


Quería hacerte solo una sugerencia fuera de tópico, cuando vayas a publicar código de ejemplo no utilices los nombres "i" o "u" para variables enteras, porque luego al ponerlas dentro de las llaves para indicar un índice, las etiquetas lo toman como parte del formato y estropean el código original.


Saludos.
  • 0

#12 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 14 noviembre 2011 - 08:51

Muy buen código Delphius, tanto para enfrentar el problema en cuestión,  así como base para muchos otros.

¿Código? Pues no puse mucho... más que nada es la declaración de una clase que esconde en un atributo el escrollo de un array.  :D
Lo único de código que expuse son los getter y setter de la propiedad vectorial. Y asumí que las implementaciones de los demás métodos pueden resultarles triviales y que se pueden fácilmente dar una idea de cómo implementar.


El patrón Adapter es sólo una propuesta de ocultar algo tras una clase. ¿Tienes un framework, clase, componentes, o lo que fuera de terceras partes y resulta un poco quisquilloso para trabajar con él? ¡Usa un Adapter! Dale a esta clase Adaptador una implementación adecuada a tus necesidades y oculta lo complejo y/o incompatible delegándolo al Adaptado.
Hay varias maneras de llegar a un Adapter.
Un Adapter simplementa actúa como una Indirección (Adapter es en realidad una extensión al concepto de Indirección y de Fabricación Pura), desacopla nuestra clase cliente de otras clases a la que no resulta conveniente vincular. La clase Adapter puede asumir el rol también (en muchas ocasiones y por lo generalmente) de Controlador, Fachada, Fábrica o Factoría que son una extensión y especialización al concepto Indirección.


La idea es simple: oculta lo incompatible o complicado tras una clase.
Y justamente este ejemplo que expuse lo extraje de una idea y diseño basado en un caso al que también tuve, y aún trabajo (y seguiré trabajando perfeccionandolo), con arrays. De hecho, mis clases actúan como una envolvente de estas estructuras y delegan en funciones básicas las operaciones sobre las estructuras de datos. Para ello tengo unas unidades que implementan funciones algebraicas y otras de cálculo. Vendría a ser como una especie de API básica.


Quería hacerte solo una sugerencia fuera de tópico, cuando vayas a publicar código de ejemplo no utilices los nombres "i" o "u" para variables enteras, porque luego al ponerlas dentro de las llaves para indicar un índice, las etiquetas lo toman como parte del formato y estropean el código original.


Saludos.

Si... lo se. Es que se me olvidó. Ya editaré mi código para corregirlo.


Saludos,
  • 0




IP.Board spam blocked by CleanTalk.