Динамический вызов метода SOAP по имени?

Я использую Delphi XE2 для связи с довольно большой службой SOAP. Я успешно импортировал wsdl, и все работает нормально. Тем не менее, я обнаружил, что пишу много подобного кода. Я хотел бы иметь общий метод, который вызывает мой веб-сервис. Мне также трудно использовать многопоточность в моем коде, как сейчас, поскольку мне приходится писать так много кода для каждого типа вызова.

Будучи больше программистом выходного дня, я далек от того, чтобы освоить все тонкости Delphi, но я думаю, что у меня, по крайней мере, есть хорошее понимание RTTI, которое, я считаю, должно использоваться для того, чтобы делать то, что я хочу.

Веб-сервис имеет около 700 различных методов, и это большая проблема. Код, сгенерированный из wsdl, имеет следующие методы:

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

По сути, существует около 700 различных типов вещей, которые можно обрабатывать, и для всех них есть методы добавления, обновления, получения, удаления и перечисления. При каждом вызове имеется соответствующий класс, который используется в качестве параметров запроса SOAP. Существует также соответствующий класс для ответа, как вы можете видеть выше.

Классы будут выглядеть примерно так (очень упрощенно):

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

Поэтому, когда я вызываю веб-службу, я делаю, например:

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;

(Да, я знаю, что должен был попробовать... наконец, и бесплатно там тоже :-))

Затем где-то еще в коде мне нужно вызвать другой метод:

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;

Поскольку я знаю, что параметр всегда является классом с именем, эквивалентным вызываемому мной методу, я хотел бы иметь возможность сделать что-то вроде приведенного ниже метакода для динамического вызова вызова. Я предполагаю, что это требует некоторой магии RTTI, но я не смог найти способ сделать это:

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

Тогда я мог бы сделать что-то вроде:

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

Кто-нибудь знает, как можно упростить мои звонки в веб-сервис?


person dahook    schedule 28.06.2012    source источник
comment
Будет интересно посмотреть, придумает ли кто-нибудь такое, но я просто написал подпрограммы-оболочки, как будто вы должны скрывать детали.   -  person mj2008    schedule 28.06.2012
comment
@dahook: Очень хорошо написан первый пост. Проголосовал. Добро пожаловать в СО.   -  person RobertFrank    schedule 28.06.2012


Ответы (1)


Это действительно странно, что я всего через несколько часов после публикации вопроса, который я пытался решить сам в течение нескольких недель, вдруг просто решается сам... Меня вдохновил просмотр SO, и я нашел это, что помог мне на этом пути: Delphi - вызов метода записи по имени.

Мой сценарий несколько специфичен, так как я вызываю методы с параметром, который имеет то же имя класса, что и сам метод. Я также написал более простую версию, которая взаимодействует с общедоступной веб-службой. Если кому-то интересно, вы можете получить код для этого здесь: http://www.hook.se/delphi/SoapDynamicInvoke.zip. Это бесполезный пример, поскольку вызовы динамических методов актуальны только тогда, когда веб-служба имеет много разных методов. Тем не менее, может быть кому-то будет интересно :-)

Ниже показано, как я решил это для своего веб-сервиса. Как уже говорилось, это довольно специфично, и код можно было бы сделать более общим, но это работает для меня.

Этот метод вызывается с объектом TRemotable, а затем веб-служба вызывается с помощью метода с тем же именем, что и имя класса объекта.

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;

И использовать функцию выше:

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;

Требуется немного приведения типов, но в моем случае я думаю, что с этим я буду в безопасности. Есть ли у кого-нибудь другие комментарии/предложения по этому поводу? Я имею в виду, это работает, но, вероятно, есть способы улучшить это.

Ваше здоровье,

Дэн

person dahook    schedule 28.06.2012