2012-06-28 26 views
14

Sto usando Delphi XE2 per comunicare con un servizio SOAP abbastanza grande. Ho importato con successo wsdl e tutto funziona perfettamente. Tuttavia, mi trovo a scrivere un sacco di codice simile. Mi piacerebbe avere un metodo generico che chiama il mio servizio web. Trovo anche difficile multithread il mio codice come è ora, dal momento che devo scrivere così tanto codice per ogni tipo di chiamata.Richiamare dinamicamente un metodo SOAP per nome?

Essendo più un programmatore del fine settimana, sono lontano dal padroneggiare i dettagli di Delphi, ma penso di avere almeno una buona comprensione di RTTI, che credo debba essere usato per fare ciò che voglio.

Il servizio Web ha circa 700 metodi diversi e questo è praticamente il problema. Il codice generato dal WSDL ha metodi come di seguito:

function addPhone(const Params: addPhone): addPhoneResponse; stdcall; 
function updatePhone(const Params: updatePhone): updatePhoneResponse; stdcall; 
function getPhone(const Params: getPhone): getPhoneResponse; stdcall; 
function removePhone(const Params: removePhone): removePhoneResponse; stdcall; 
function listPhone(const Params: listPhone): listPhoneResponse; stdcall; 
function addStuff(const Params: addStuff): addStuffResponse; stdcall; 
function updateStuff(const Params: updateStuff): updateStuffResponse; stdcall; 
... 
... about 700 more of the above 

In sostanza, non v'è di circa 700 diversi tipi di cose che può essere gestito, e non v'è aggiungere, aggiornare, ottenere, rimuovere e list-metodi per tutti loro . Con ogni chiamata, esiste una classe corrispondente che viene utilizzata come parametri per la richiesta SOAP. C'è anche una classe corrispondente per la risposta, come puoi vedere sopra.

Le classi dovrebbero essere simile (molto semplificata):

addStuff = class 
    private 
    FStuff: string; 
    published 
    property stuff: string Index (IS_UNQL) read FStuff write FStuff; 
    end; 

Così, quando io chiamo il servizio web che faccio, per esempio:

procedure CreateStuff; 
var 
    req: addStuff; 
    res: addStuffResponse; 
    soap: MyWebServicePort; 
begin 
    // Use the function in the wsdl-generated code to create HTTPRIO 
    soap := GetMyWebServicePort(false,'',nil); 
    // Create Parameter Object 
    req := addPhone.Create; 
    req.stuff := 'test'; 
    // Send the SOAP Request 
    res := soap.addStuff(req); 
end; 

(Sì, lo so che avrei dovuto provare ..finally e Free in là pure :-))

Quindi, da qualche altra parte nel codice ho bisogno di chiamare un metodo diverso:

Poiché so che il parametro è sempre una classe con un nome equivalente al metodo che chiamo, mi piacerebbe essere in grado di fare qualcosa come il metacode di seguito per richiamare dinamicamente la chiamata. Credo che richiede un po 'di magia RTTI ma have'nt stato in grado di trovare un modo per farlo:

procedure soapRequest(Param: Something; var Response: Something); 
begin 
    soap := GetMyWebServicePort(false,'',nil); 
    Response := soap.DynamicInvoke(Param.ClassName, Param); 
end 

allora avrei potuto fare qualcosa di simile:

soapRequest(VarOfTypeAddStuff,VarOfTypeAddStuffResponse) 
soapRequest(VarOfTypeListStuff,VarOfTypeListStuffResponse) 
... 

Qualcuno ha un'idea di come il mio le chiamate al webservice possono essere semplificate?

+0

sarà interessante per vedere se qualcuno si presenta con tali, ma ho appena scritto le routine di wrapper come si deve "nascondere" i dettagli. – mj2008

+3

@dahook: il primo post scritto molto bene. Votato. Benvenuti in SO. – RobertFrank

risposta

4

E 'davvero strano che solo poche ore dopo aver postato una domanda che ho cercato di risolvere da solo per settimane, tutto ad un tratto, appena risolto da solo ... Mi sono ispirato a guardarmi intorno su SO, e Ho trovato questo che mi ha aiutato lungo la strada: Delphi - Invoke Record method per name.

Il mio scenario è un po 'specifico, dal momento che sto chiamando i metodi con un parametro che ha lo stesso nome di classe del metodo stesso. Ho anche scritto una versione più semplice che comunica con un servizio web pubblico. Se qualcuno è interessato, puoi ottenere il codice per quello qui: http://www.hook.se/delphi/SoapDynamicInvoke.zip. È una specie di esempio inutile poiché fare chiamate di metodi dinamici è rilevante solo quando il servizio web ha molti metodi diversi. Tuttavia, potrebbe essere interessante per qualcuno :-)

Di seguito è come ho risolto questo per il mio servizio web. Come detto, è abbastanza specifico e il codice potrebbe essere reso più generico ma questo funziona per me.

Questo metodo viene chiamato con un oggetto TRemotable e quindi il servizio Web viene chiamato con il metodo con lo stesso nome del nome di classe dell'oggetto.

function soapRequest(Param: TRemotable): TValue; 
var 
    soap: AXLPort; 
    C: TRttiContext; 
    T: TRttiType; 
    M: TRttiMethod; 
    SoapParam: TArray<TValue>; 
    TVres: TValue; 
    soap: MyWebServicePort; 
begin 
    // Use the function in the wsdl-generated code to create HTTPRIO 
    soap := GetMyWebServicePort(false,'',nil); C := TRttiContext.Create; 
    T := C.FindType('MyWebService.MyWebServicePort'); 
    M := T.GetMethod(Param.ClassName); 
    SetLength(SoapParam,1); 
    SoapParam[0] := TValue.From(Param); 
    TVres := M.Invoke(TValue.From<IInterface>(soap), SoapParam); 
    Result := TVres; 
end; 

E per utilizzare la funzione di cui sopra:

procedure DoSomeSoapCalls(Sender: TObject); 
var 
    req1: getStuff 
    res1: getStuffResponse; 
    req2: addStuff; 
    res2: addStuffResponse; 
    res: TValue; 
begin 
    //Request #1 
    req1 := getStuff.Create; 
    req1.stuffToGet := 'abc'; 
    try 
    res := soapRequest(req1); 
    res1 := getStuffResponse(res.AsObject); 
    finally 
    req1.Free; 
    end; 
    Writeln(res1.someproperty); 
    FreeAndNil(res1); 

    //Request #2 
    req2 := addStuff.Create; 
    req2.StuffToAdd := 'cde'; 
    try 
    res := soapRequest(req2); 
    res2 := addStuffResponse(res.AsObject); 
    finally 
    req2.Free; 
    end; 
    Writeln(res2.result); 
    FreeAndNil(res2); 
end; 

C'è un po 'di fusione di caratteri necessari, ma nel mio caso penso che sarò abbastanza sicuro con quello. Qualcuno ha altri commenti/suggerimenti riguardo questo? Voglio dire, funziona, ma ci sono probabilmente dei modi per migliorarlo.

Cheers,

Dan

Problemi correlati