segunda-feira, 12 de abril de 2010

Como executar um método pelo Nome

Olá!

Este é o tipo de dica que a gente acha que nunca vai usar.

:D

Eu uso o KBM para construir sistemas em N Camadas: http://www.components4programmers.com/.

Para a construção de um RPC ( estou indo direto ao ponto, em caso de dúvidas sobre o assunto me contacte ) a sugestão é encadear um monte de IF como no exemplo abaixo:


// Master request processor.
//--------------------------

function TkbmRPCDatabase.ProcessRequest(const Func:string; const ClientIdent:TkbmMWClientIdentity; const Args:array of Variant):Variant;
var
afunc : string;
begin
  Self.GerarLog(Format('Func: [%s]',[Func]), 0001, ctLOGGExpert,rlLow);

  try
    AFunc:=UpperCase(Func);
    if AFunc='TESTE' then
       Result := PerformTESTE(ClientIdent,Args)
    else Result:=inherited ProcessRequest(Func,ClientIdent,Args);
  except
    on E: Exception do
    begin
      Self.GerarLog(DescreverException(E),0001, ctLOGGExpert,rlLow );
      raise;
    end;
  end;
end;

Ou seja, se eu tiver n funções, teria que encadear um if..else que na minha opnião fica asqueroso de se dar manutenção além de obviamente não ser "performático".

A solução definitiva apareceu nesse tópico: http://delphi.about.com/cs/adptips2004/a/bltip0204_3.htm

Note o seguinte: O seu método tem que estar na parte published da sua classe. Em outras sessões não rola.

Veja como ficou o código:

// Master request processor.
//--------------------------

function TkbmRPC_CSAS.ProcessRequest(const Func:string; const ClientIdent:TkbmMWClientIdentity; const Args:array of Variant):Variant;
var
sFunc    : string;
rRoutine : TMethod;
_perform : TPerformFunction;
begin
  Self.GerarLog(Format('Executando: [%s] - [%s]',[Func,ClientIdent.RemoteLocation]),322382429,ctLOGInformation,rlMiddle);

  sFunc := Format('Perform%s',[Func]);
  rRoutine.Data := Pointer(Self);
  rRoutine.Code := Self.MethodAddress(sFunc);

  if (Assigned(rRoutine.Code)) then
  begin
    _perform := TPerformFunction(rRoutine);
    Result := _perform(ClientIdent,Args);
  end else
  begin
    inherited ProcessRequest(Func,ClientIdent,Args);
  end;

end;

Para isso tudo funcionar, tive que criar um tipo especial:

interface

uses
  kbmMWSecurity;

type

{$M+}

  ///Usado para permitir uma chamada dinâmica às funções dos RPCs
  TPerformFunction = function(ClientIdent:TkbmMWClientIdentity; const Args:array of Variant): Variant of object;

Minha lista de blogs