2012-06-28 12 views
14

Ich verwende Delphi XE2, um mit einem ziemlich großen SOAP-Dienst zu kommunizieren. Ich habe erfolgreich die WSDL importiert und alles funktioniert gut. Ich finde jedoch, dass ich viel ähnlichen Code schreibe. Ich möchte eine generische Methode haben, die meinen Webservice aufruft. Ich finde es auch schwierig, meinen Code so zu multithread wie es jetzt ist, da ich so viel Code für jede Art von Anruf schreiben muss.Dynamische Aufrufen einer SOAP-Methode nach Name?

Da ich eher ein Wochenendprogrammierer bin, bin ich weit davon entfernt, die Delphi zu beherrschen, aber ich denke zumindest, dass ich RTTI gut verstehe, was meiner Meinung nach dazu dienen muss, das zu tun, was ich will.

Der Web-Service hat etwa 700 verschiedene Methoden und das ist so ziemlich das Problem. Der Code erzeugt aus der WSDL-Datei verfügt über Methoden wie folgt:

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 

Grundsätzlich gibt es etwa 700 verschiedene Arten von Dingen, die behandelt werden können, und es gibt hinzufügen, aktualisieren, entfernen und list-Methoden für sie alle . Bei jedem Aufruf gibt es eine entsprechende Klasse, die als Parameter für die SOAP-Anfrage verwendet wird. Es gibt auch eine entsprechende Klasse für die Antwort, wie Sie oben sehen können.

Die Klassen würden in etwa so aussehen (sehr vereinfacht):

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

Also, wenn ich rufe den Webdienst ich zum Beispiel so tun:

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; 

(Ja, ich weiß, ich versuche haben sollte ..finally und Free da drin auch :-))

Dann woanders im Code muss ich eine andere Methode aufrufen:

Da ich weiß, dass der Parameter immer eine Klasse mit einem Namen ist, der der Methode entspricht, die ich anrufe, möchte ich in der Lage sein, etwas wie den Metacode unten zu tun, um den Aufruf dynamisch aufzurufen. Ich denke, es etwas RTTI Magie erfordert aber ich have'nt einen Weg zu finden, in der Lage, es zu tun:

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

Dann etwas wie ich tun konnte:

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

Hat jemand eine Idee, wie meine Anrufe zum Webservice können vereinfacht werden?

+0

Es wird interessant sein zu sehen, ob jemand mit einer solchen kommt, aber ich schrieb nur Wrapper-Routinen, wie Sie die Details zu „verstecken“ haben. – mj2008

+3

@dahook: Sehr schön geschriebener erster Beitrag. Abgestimmt. Willkommen bei SO. – RobertFrank

Antwort

4

Es ist wirklich komisch, dass ich nur ein paar Stunden nach dem Posten einer Frage, die ich seit Wochen selbst zu lösen versuche, einfach von mir selbst gelöst werde ... Ich wurde inspiriert, indem ich mich auf SO umschaute Ich fand das, was mir auf dem Weg half: Delphi - Invoke Record method per name.

Mein Szenario ich etwas spezifisch, da ich die Methoden mit einem Parameter aufrufen, der den gleichen Klassennamen wie die Methode selbst hat. Ich habe auch eine einfachere Version geschrieben, die mit einem öffentlichen Webdienst kommuniziert. Wenn jemand interessiert ist, können Sie den Code für diesen hier erhalten: http://www.hook.se/delphi/SoapDynamicInvoke.zip. Es ist ein wenig sinnloses Beispiel, da dynamische Methodenaufrufe nur dann relevant sind, wenn der Webdienst viele verschiedene Methoden verwendet. Trotzdem könnte es für jemanden interessant sein :-)

Unten ist, wie ich das für meinen Webservice gelöst habe. Wie gesagt, es ist ziemlich spezifisch und der Code könnte generischer gemacht werden, aber das funktioniert für mich.

Diese Methode wird mit einem TRemotable-Objekt aufgerufen, und dann wird der Webdienst mit der Methode mit demselben Namen wie der Klassenname des Objekts aufgerufen.

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; 

und die Funktion oben zu verwenden:

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; 

Es ist ein bisschen wie eine Schublade gesteckt notwendig, aber in meinem Fall denke ich, dass ich damit ziemlich sicher sein werden. Hat jemand andere Kommentare/Vorschläge diesbezüglich? Ich meine, das funktioniert, aber es gibt wahrscheinlich Möglichkeiten, es zu verbessern.

Cheers,

Dan

Verwandte Themen