Pytanie Dynamiczne wywoływanie metody SOAP według nazwy?


Używam Delphi XE2 do komunikacji z dość dużą usługą SOAP. Pomyślnie zaimportowałem kod WSDL i wszystko działa dobrze. Jednakże, piszę dużo podobnych kodów. Chciałbym mieć ogólną metodę, która wywołuje moją usługę sieciową. Również trudno jest wielowątkowy mój kod, ponieważ jest teraz, ponieważ muszę napisać tyle kodu dla każdego rodzaju połączenia.

Będąc raczej weekendowym programistą, daleko mi do opanowania Delphi, ale myślę, że przynajmniej mam uczciwe zrozumienie RTTI, które, jak sądzę, musi być wykorzystywane do robienia tego, co chcę.

Serwis internetowy ma około 700 różnych metod i to jest prawie problem. Kod wygenerowany z pliku wsdl ma następujące metody:

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

Zasadniczo istnieje około 700 różnych typów rzeczy, którymi można się posługiwać, a także dodawać, aktualizować, uzyskiwać, usuwać i listować metody dla nich wszystkich. Przy każdym wywołaniu istnieje odpowiednia klasa, która jest używana jako parametry do żądania SOAP. Istnieje również odpowiednia klasa odpowiedzi, jak widać powyżej.

Klasy wyglądałyby tak (bardzo uproszczone):

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

Więc kiedy zadzwonię do usługi sieciowej, robię na przykład:

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;

(Tak, wiem, że powinienem spróbować ... i Finalnie też tam :-))

Następnie, gdzieś indziej w kodzie, muszę wywołać inną metodę:

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

Ponieważ wiem, że parametr jest zawsze klasą o nazwie odpowiadającej metodzie, którą wywołuję, chciałbym móc wykonać coś takiego jak metakod poniżej, aby dynamicznie wywoływać wywołanie. Myślę, że wymaga to trochę magii RTTI, ale nie byłem w stanie znaleźć sposobu, aby to zrobić:

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

Potem mógłbym zrobić coś takiego:

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

Czy ktoś ma pomysł, w jaki sposób można uprościć moje połączenia z serwisem internetowym?


14
2018-06-28 15:11


pochodzenie


Interesujące będzie sprawdzenie, czy ktoś taki nie wymyśli, ale właśnie pisałem procedury, takie jak "ukrywanie" szczegółów. - mj2008
@dahook: Bardzo ładnie napisany pierwszy post. Zagłosowałem w górę. Witamy w SO. - RobertFrank


Odpowiedzi:


To naprawdę dziwne, że kilka godzin po napisaniu pytania, które próbowałem rozwiązać od tygodni, nagle sam się rozwiązałem ... Zainspirowało mnie rozejrzenie się po SO, i znalazłem to, pomógł mi po drodze: Delphi - metoda Invoke Record na imię.

Mój scenariusz jest dość specyficzny, ponieważ wywołuję metody z parametrem, który ma tę samą nazwę klasy, co sama metoda. Napisałem też prostszą wersję, która komunikuje się z publiczną usługą sieciową. Jeśli ktoś jest zainteresowany, możesz uzyskać kod dla tego tutaj: http://www.hook.se/delphi/SoapDynamicInvoke.zip. Jest to bezużyteczny przykład, ponieważ wykonywanie dynamicznych wywołań metod jest istotne tylko wtedy, gdy usługa sieciowa ma wiele różnych metod. Niemniej może to być interesujące dla kogoś :-)

Poniżej znajduje się sposób rozwiązania tego problemu dla mojej usługi internetowej. Jak już powiedziano, jest to dość specyficzne i kod może stać się bardziej ogólny, ale to działa dla mnie.

Ta metoda jest wywoływana za pomocą obiektu TREMotable, a następnie usługa sieciowa jest wywoływana za pomocą metody o tej samej nazwie, co nazwa klasy obiektu.

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;

I użyć powyższej funkcji:

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;

Konieczna jest odrobina typowania, ale w moim przypadku myślę, że będę z tym całkiem bezpieczny. Czy ktoś ma jakieś inne komentarze / sugestie dotyczące tego? Mam na myśli, że to działa, ale prawdopodobnie istnieją sposoby, aby go ulepszyć.

Twoje zdrowie,

Dan


4
2018-06-28 22:51