Share Button

Nestes primeiros meses de 2017, o grupo MVCBr dedicou a maior parte do tempo em implementar um servidor OData que permite o acesso a base de dados utilizando protocolo RESTful via HTTP.

FIREBIRD !!!

Sim….. o servidor OData  implementado no MVCBr é um servidor que expõe recursos (resources) armazenados em um servidor FIREBIRD 3.0

Simplificando, o servidor MVCBrServer é um servidor RESTful que utiliza o protocolo OData que suporta o Firebird3.

Arquitetura do Servidor

O servidor é um servidor implementado utilizando componentes INDY para fazer o processamento das requisições. Ao receber a chamada do CLIENTE o motor INDY passa para o framework  “Delphi MVC Framework” implementado pelo “Daniele Teti”. Seguindo os padrões RESTful, o servidor analisa o tipo de pacote que esta recebendo (GET, PUT, POST, PATCH, DELETE, OPTIONS) e passa a requisição para um PARSER OData implementado no framework MVCBr. Ao receber a requisição o PARSER prepara a requisição que será feito ao banco de dados (ex: select * from produtos), avalia o “metadata” contendo as regras de acesso e constrói a solicitação através da estrutura do FireDAC do Delphi, que remete ao driver o FIREBIRD. Recebendo o retorno tudo é empacotado utilizando JSON e devolvido para o cliente.

 

O “metadata” – Onde o modelo relacional é descrito

Junto com o projeto MVCBr irá encontrar um banco FIREBIRD3  (MVCBr.fdb)  que possui uma estrutura básica de teste utilizada no “framework”

Configurar o “databases.conf” do firebird:

 #
# Live Databases:
#
mvcbr=[path]/mvcbr.fdb

Como configurar um resource

Resource é o identificar a ser utilizado no HTTP para acessar um determinado recurso do banco de dados.
Veja o exemplo:

    {
      "resource": "produtos",
      "collection": "produtos",
      "keyID": "codigo",
      "maxpagesize": 100,
      "fields": "*",
      "method": "GET,PATCH,POST,PUT,DELETE",
      "relations": [
        {
          "resource": "grupos",
          "sourceKey": "grupo",
          "targetKey": "codigo",
          "join": "left join grupos on (produtos.grupo=grupos.grupo)"
        }
      ]
    }
  • resource:  apelido para o URL utilizada no HTTP
  • collection: nome da tabela fisica no banco de dados
  • keyID: coluna de acesso rápido ás linhas da tabela    ex: http://…./OData/OData.svc/produtos(‘789112313311’)
  • maxpagesize: número máximo de linhas a retornar caso não seja indicado o comando   $top
  • fields: lista de colunas a retornar quando o comando  $select não for indicado
  • method: quais as permissões serão publicadas aos clientes
  • relations: quais relacionamento podem ser executados com “resource” corrente  ( é um DETAIL)
    • relations.resource: qual o apelido do relacionamento com o resource MASTER
    • relations.sourceKey: qual a coluna de recionamento no resource MASTER
    • relations.targetKey: qual a coluna de relacionamento no resource DETAIL
    • relations.join: utilizado para JOINs mais complexos ignorando “sourceKey” e “targetKey”

Listagem completa do metadata de exemplo

{
  "@odata.context": "http://localhost:8080/OData/OData.svc",
  "__comment": "Services list all resource available to OData.Service",
  "OData.Services": [
    {
      "resource": "produtos",
      "collection": "produtos",
      "keyID": "codigo",
      "maxpagesize": 100,
      "fields": "*",
      "method": "GET,PATCH,POST,PUT,DELETE",
      "relations": [
        {
          "resource": "grupos",
          "sourceKey": "grupo",
          "targetKey": "codigo",
          "join": "left join grupos on (produtos.grupo=grupos.grupo)"
        }
      ]
    },
    {
      "resource": "grupos",
      "collection": "grupos",
      "keyID": "codigo",
      "fields": "*",
      "method": "GET,PATCH,DELETE,PUT,POST",
      "maxpagesize": 100,
      "relations": [
        {
          "resource": "produtos",
          "sourceKey": "codigo",
          "targetKey": "grupo",
          "join": "join produtos on (grupos.codigo=produtos.grupo)"
        }
      ]
    },
    {
      "resource": "fornecedores",
      "collection": "fornecedores",
      "maxpagesize": 100,
      "fields": "*",
      "keyID": "codigo"
    },
    {
      "resource": "clientes",
      "collection": "clientes",
      "keyID": "codigo",
      "method": "GET,POST,PATCH,UT,DELETE",
      "searchFields": "nome",
      "maxpagesize": 100,
      "fields": "*"
      "relations": [
        {
          "resource": "vendas",
          "join": "join vendas on (vendas.cliente=clientes.codigo)"
        },
        {
          "resource": "vendas_item",
          "join": "join vendas a  on (clientes.codigo=a.cliente) join vendas_item b on (b.documento=a.documento)"
        }
      ]
    },
    {
      "resource": "vendas",
      "collection": "vendas",
      "maxpagesize": 100,
      "keyID": "documento",
      "fields": "*",
      "method": "GET,POST,PATCH,PUT,DELETE"
    },
    {
      "resource": "vendas_item",
      "collection": "vendas_item",
      "maxpagesize": 100,
      "keyID": "documento"
      "method": "GET,POST,PATCH,PUT,DELETE"
    }

  ]


}

 

Share Button

 

Chegar a um CRUD é um grande desafio. Associar um MVC no desenvolvimento e a utilização de RESTful para persistir os dados (com OData) só faz crescer o desafio.

O desafio vem do fato de tudo ser novidade e forçar a quebrar paradigmas antes impensáveis utilizando a ferramenta do dia-a-dia, o Delphi.

Vamos ligar os componentes:

TIdHTTPRestClientTIdHTTPRestClient é responsável em fazer a
comunicação entre o cliente e o servidor RESTful.

É o canal para buscar dados no servidor, bem como enviar dados para o servidor (é um componente de transporte).

ver mais…

 

 

Os preenchimento das propriedades do RestClient pode ser feito manualmente, ou ainda, de forma automática se for utilizado em conjunto com um TODataBuilder.

 

ODataCom o TODataBuilder permite construir a soliciatação GET a ser enviada ao servidor OData.

Ligar ao TODataBuilder a propriedade RestClient com o TIdHTTPRestClient. Com isto o comando construído no Builder será repassado (preenchido automático) no RestClient.  ver mais….

 

 

 

O passo seguinte é ligar o TODataDatasetAdpater.

TODataDatasetAdapterO Adapter recebe os dados do RestClient e liga com o Dataset.

Ligar o Builder, Dataset e o ResponseJSON( RestClient).

Para obter os dados com os servidor, chamar o método execute. ver mais…

Feito estas ligações nos componentes, já é possível chamar o EXECUTE do Adapter que irá preencher o Dataset com a resposta do servidor.

Para enviar um INSERT, DELETE ou UPDATE para o servidor ( ver exemplo).

O código a seguir mostra como montar os dados a serem enviado para o servidor persistir as alterações.

Os seguintes métodos do Adapter são importantes, como no exemplo:

ODataDatasetAdapter1.AddRowSet(rctInserted, DataSet); /// põe na pilha um INSERT;
ODataDatasetAdapter1.AddRowSet(rctModified, DataSet);  /// põe na pilha um UPDATE
ODataDatasetAdapter1.AddRowSet(rctDeleted, DataSet);  /// põe na pilha um DELETE;
ODataDatasetAdapter1.ApplyUpdates(nil, rmPATCH);  /// atualiza o servidor com os dados da pilha;
ODataDatasetAdapter1.Execute;   /// faz um SELECT no banco de dados
ODataDatasetAdapter1.ClearChanges;  /// limpa a pilha

 

Código de exemplo que esta no projeto MVCBr.  no git

{ //************************************************************// }
{ //                                                            // }
{ //         Código gerado pelo assistente                      // }
{ //                                                            // }
{ //         Projeto MVCBr                                      // }
{ //         tireideletra.com.br  / amarildo lacerda            // }
{ //************************************************************// }
{ // Data: 10/03/2017 21:21:54                                  // }
{ //************************************************************// }
///
/// Uma View representa a camada de apresentação ao usuário
/// deve esta associado a um controller onde ocorrerá
/// a troca de informações e comunicação com os Models


....
    DBGrid1: TDBGrid;

    DataSource1: TDataSource;
    /// Componente de Envio de mensagem HTTP para o servidor OData RESTful
    IdHTTPRestClient1: TIdHTTPRestClient;
    /// Construção de URL a ser enviado ao servidor
    ODataBuilder1: TODataBuilder;
    /// Desempacota a resposta do servidor e preenche o Dataset
    ODataDatasetAdapter1: TODataDatasetAdapter;

    /// Dataset que recebe a resposta do servidor - preenchida com o Adpater
    FDMemTable1: TFDMemTable;   /// FireDAC
    Button1: TButton;  ///  botão GET
    Button2: TButton;  ///  botão ApplayUpdates
....
  private
    FLoading: boolean;  /// Flag para marcar se esta fazendo carga inicial da tabela.
    FState: TDataSetState;  /// Guarda o STATE do Dataset a ser usando no AfterPost
  protected
  public
    { Public declarations }
...
  end;

Implementation

{$R *.DFM}

.....
/// No evento AfterPost do Dataset, armazena na lista de
/// alterações a serem enviadas para o servidor OData RESTful
procedure TRestODataAppView.FDMemTable1AfterPost(DataSet: TDataSet);
begin
  if not FLoading then /// trata somente os registros alterados pelo usuário... (para não pegar os dados iniciais na carga da resposta do servidor)
    case FState of
      dsInsert:  /// empilha os dados de INSERT - inserted
        ODataDatasetAdapter1.AddRowSet(rctInserted, DataSet);
      dsEdit:    /// empilha os dados do POST - modified
        ODataDatasetAdapter1.AddRowSet(rctModified, DataSet);
    end;

end;

/// No ApplyUpdates do Dataset despachar para o servidor
/// as alterações armazenadas para serem notificadas
/// ao servidor
procedure TRestODataAppView.FDMemTable1BeforeApplyUpdates(DataSet: TFDDataSet);
begin
  // envia os dados alterados para o servidor OData RESTful
  ODataDatasetAdapter1.ApplyUpdates(nil, rmPATCH);
end;

procedure TRestODataAppView.FDMemTable1BeforeDelete(DataSet: TDataSet);
begin
  /// empilha os dados a serem de DELETE - deleted a ser enviado ao servidor
  ODataDatasetAdapter1.AddRowSet(rctDeleted, DataSet);
end;

procedure TRestODataAppView.FDMemTable1BeforePost(DataSet: TDataSet);
begin
  /// guarda o estado da linha (dsEdit, dsInsert) para uso no AfterPost
  FState := DataSet.State;
end;

procedure TRestODataAppView.FormCreate(Sender: TObject);
begin
  /// um exemplo de como adicionar CustomHeaders a mensagem que será enviada ao servidor
  IdHTTPRestClient1.IdHTTP.Request.CustomHeaders.AddValue('token', 'abcdexz');
end;

procedure TRestODataAppView.Button1Click(Sender: TObject);
begin
  FLoading := true;  /// controle para não gerar linhas de alterações na pilhas
  try
    ///  executa um GET no servidor RESTful
    ///  o servidor fazer um SELECT e devolve o conteúdo as linhas do SELECT
    ODataDatasetAdapter1.Execute;
    ///  limpa alterações que estejam armazenadas
    ODataDatasetAdapter1.ClearChanges;
  finally
    FLoading := false;
  end;
end;

procedure TRestODataAppView.Button2Click(Sender: TObject);
begin
  FDMemTable1.ApplyUpdates();   ///  processa a alteração do Dataset
end;


 

Share Button

TODataBuilder é um construtor de URI para chamada ao OData Server.

OData

O construtor  é uma facilidade para criar a URI de chamada ao OData através da interface gráfica do Delphi onde:

  • BaseURL – Servidor e porta do OData;
  • Count – indica se é para retornar o número total de linhas do select – usado para paginação;
  • Expand – lista de Resource DETAIL   (não implementado no MVCBrServer neste momento);
  • Filter – filtro de contrução auxiliar para o cláusula WHERE de busca no banco de dados (aceita os operadores do OData);
  • Resource – lista de Collections/Tabelas e respectivos parâmetros aninhadas ( max 3 );
  • Select – lista de colunas a retornar pelo consulta no servidor;
  • Service – nome do serviço no servidor;
  • ServicePreffix – prefixo de composição da URI associado ao Serviço;
  • SkipRows – indica quantas linhas saltar antes de retornar o conjunto de linhas (default=0);
  • TopRows – indica a quantidade de linhas a serem retornadas (default=0  todas);

O componente expõe o metódo “execute” que tem por atribuição preencher os parâmetros do TIdHTTPRestClient e chamar o seu “execute”;

RestClient | Introdução ao OData

 

Share Button

TIdHTTPRestClient é um cliente RESTful implementado sobre o componente INDY  TIdHTTP de uso geral.

TIdHTTPRestClient

 

O procedimento “execute” do componente envia uma solicitação ao servidor HTTP e aguarda resposta pela propriedade “content”.

Propriedades

  • BaseURL – servidor e porta de acesso;
  • Method – identifica o método a ser executa no servidor;
  • Resource – URL de acesso;
  • ResourcePreffix – Serviço de acesso no servidor;

 

Informações adicionais de HEADER deve ser enviado através da propriedade do componente “IdHTTP” que expõe o componente TIdHTTP instanciado internamente.

Exemplo de envio de “token” no HEADER:

   IdHTTPRestClient1.IdHTTP.Request.CustomHeaders.AddValue('token','abcdexz');

Se necessário obter informações adicionais é possível obter a partir de documentação do TIdHTTP e pode ser aplicado a propriedade IdHTTPRestClient1.IdHTTP…

Introdução ao OData

Share Button

Nos textos anteriores foi possível destacar como fazer INSERT, DELETE e UPDATE – cada um individualmente em comandos separados.

Aqui vamos mostrar o METHOD   PATCH. Na especificação padrão do RESTful o PATCH efetua um UPDATE no banco de dados. Qual então a diferença entre PUT e PATCH ? Regra geral no OData, o comando PUT faz update de todas as colunas da tabela, enquanto o PATCH faz igualmente um update, no entanto realiza atualização somente nas colunas indicadas no BODY.method-patch

No MVCBrServer, o PUT e o PATCH possuem as mesmas funcionalidades, ou seja, atualizam somente as colunas indicadas no BODY como já visto no texto que discorremos sobre o METHOD PUT.

Uma variação implementada no MVCBrServer é a possibilidade do comando PATCH executar múltiplas funções e aceitar enviar INSERT, DELETE e UPDATE dentro de um mesmo ARRAY com as linhas de comando.

Para executar múltiplas funcionalidade, cada linha deve ser indicada com uma propriedade “rowstate” com um dos valores: “modified”, “inserted”, “deleted”.

Exemplo:

url: http://localhost:8080/OData/OData.svc/vendas()
body:
[
   { "rowstate":"deleted","data":"2017-03-17","documento":3},
   { "rowstate":"inserted","data":"2017-03-17","documento":3,"cliente":1,"total":10},
   { "rowstate":"modified","data":"2017-03-17","documento":3,"cliente":2,"total":10}
]

 

Este BODY envia 3 comandos no mesmo ARRAY que serão executados na seguinte ordem:

  1. na linha 1 o “rowstate”:”deleted” indica que a linha deve ser processada como um comando DELETE;
  2. na linha 2 o “rowstate”:”inserted” indica que a linha é um INSERT;
  3. na linha 3 o “rowstate”:”modified” deve processar um UPDATE para a linha;
Resposta:
{"@odata.context":"\/OData\/OData.svc\/vendas()","StartsAt":"2017-03-17T20:45:46.541Z",
 "@odata.count":"3","EndsAt":"2017-03-17T20:45:46.559Z"}

Em relação ao controle de transação da operação o controle ocorre no mesmo formato que já tratamos aqui ACID , ou seja – tudo ou nada.

 

Introdução ao OData

Share Button

A execução de um UPDATE no banco de dados envolve duas categorias de informações. A primeira delas é uma lista de colunas a atualizar no servidor, na segunda parte de importância é a indicação de quais linhas serão envolvidas na atualização (a WHERE).

Com base na especificação OData para RESTful, o METHOD PUT indica que o cliente deseja fazer uma atualização da tabela. As colunas a serem atualizadas devem ser enviadas no BODY da mensagem ao servidor RESTful. O servidor MVCBrServer esta preparado para receber uma linha simples de colunas/valores, bem como um ARRAY com um conjunto de linhas.

Possibilidades  de formato do BODY para atualizar a(s) linha(s):

  1. Uma linha simples:
      { "id" : "1" ,  "nome":"DESCRICAO TESTE" }
  2. Um ARRAY com uma lista de linhas:
      
          [  { "id": "1",  "nome": "DESCRICAO 1"},
             { "id": "2",  "nome": "DESCRICAO 2"},....
          ] 
    

Se o BODY indica os valores das colunas a serem atualizadas no servidor, próximo passo é tratar a seleção das linhas que iram receber atualização já que em geral queremos atualizar uma linha ou um conjunto delas e raramente desejamos atualizar todas as linhas.
Preferencialmente a definição da WHERE deve ser indicada como parâmetro para o RESOURCE (a tabela ou coleção):

     http://localhost:8080/OData/OData.svc/produtos(id='1')  -> aplica atualiza sobre a linha com ID = "1"
     
     caso o RESOURCE requeira mais de uma coluna - pode-se separa-las por vírgulas

     http://localhost:8080/OData/OData.svc/produtos(id='1',outra='x')  -> aplica atualiza sobre a linha com ID = "1"  AND outra="x"

Caso a coluna não seja indicada, o MVCBrServer irá utilizar a chave indicada no metadata (modelo de dados).

Quando o BODY indica uma lista de linhas em um ARRAY – não é possível indicar a chave nos parâmetros já que cada linha possui uma chave diferente (WHERE). Neste caso o servidor irá utilizar o seu “keyID” do metadata e aplicar o valor de cada linha na WHERE para escolher as linhas que irão receber atualização ou seja, as linhas enviadas no ARRAY devem conter as colunas correspondente a chave do RESOURCE.

Ao caso cabe uma questão – A especificação OData indica que no comando PUT, todas as colunas devem ser enviadas para o servidor. A implementação no MVCBrServer não possui esta exigência – é possível enviar somente as colunas da chave e as colunas a serem atualizadas, ignorando as colunas que não foram enviadas na lista. Nos casos que for necessário enviar um NULL para uma coluna, este desejo deve constar na linha enviada… Ex: {…., “colunaxxx”: NULL, … }

Introdução ao OData | INSERT | DELETE |

Share Button

O texto anterior mostrei um exemplo de como enviar um INSERT para o servidor usando o METHOD POST padrão RESTful utilizado no MVCBrServer.

Agora vamos olhar como enviar comando DELETE (METHOD DELETE) para o servidor e excluir um linha na tabela.

Há duas possibilidade para excluir registros de uma tabela na estrutura do servidor MVCBrServer. A primeira é enviar uma única linha diretamente pela URI ao servidor indicando o RESOURCE e como parâmetro o identificador padrão da linha – em geral ligado a chave primária:

  • http://localhost:8080/OData/OData.svc/grupos(’08’) – com METHOD DELETE, exclui a(s) linha(s) com chave ’08’ – neste formato o servidor irá utilizar como chave para encontrar a linha a excluir o “keyID” da tabela indicado no metadata – Um cuidado a observar é que neste formato pode ocorrer do metadata se referir a mais de uma linha e neste caso o servidor irá excluir todas as linhas que contenham a mesma informação – exemplo: se for o documento da venda e o RESOURCE for os itens irá excluir todos os itens da venda que contenham o mesmo documento;
  • http://localhost:8080/OData/OData.svc/grupos(grupo=’08’) – neste modelo o servidor irá utilizar a coluna indicada para selecionar as linhas a serem escolhidas o que permite melhor controle pelo “Coder”. Caso seja necessário mais de uma coluna, basta separar por vírgula ex:  …/ItemDaVenda(dcto=’00001′,ordem=1);

Outro formato é o envio no “BODY” com um ARRAY contendo uma lista de linhas a serem excluídas:

	http://localhost:8080/OData/OData.svc/ItemDaVenda

        no BODY enviar o JSONArray:
           [ { "dcto":"00001", "ordem":1},
             { "dcto":"00002", "ordem":1}
              ....
           ]

Introdução ao OData | Fazendo INSERT

Share Button

Tomando emprestado do padrão RESTful, para enviar um INSERT para o banco de dados com o protocolo OData é preciso formatar uma mensagem com METHOD POST e enviar no BODY da mensagem o JSON correspondente aos dados das colunas a serem inseridas no banco de dados.

 

POST_Cliente

 

No exemplo, o METHOD POST enviou uma linha contendo o registro a persistir no banco de dados. Como a repetição de envios de comandos ao servidor pode se tornar de alto custo, dado ao uso de conectividade de baixa velocidade ou instável – o recomendado é enviar um pacote de dados juntos para que sejam executados no servidor. Para isto, o servidor MVCBrServer prevê a possibilidade de receber um   ARRAY JSON com uma lista de registros a serem inseridos.

Sendo assim, é possível enviar um pacote de dados no seguinte formado:

URL-POST: http://localhost:8080/OData/OData.svc/clientes

[
{"codigo":9999997,
"nome":"Jose da Silva",
"fone":"055-111-12312",
"cidade":"São Paulo"
},

{"codigo":9999998,
"nome":"Jose da Silva8",
"fone":"055-111-12312",
"cidade":"São Paulo"
},

{"codigo":9999999,
"nome":"Jose da Silva9",
"fone":"055-111-12312",
"cidade":"São Paulo"
}
]

Codigo GIT MVCBr

Primeira Parte

FB

 

Share Button

Um servidor Datasnap é na excência um servidor HTTP nativo…. SIM é possível implementar um HTTP Server no mesmo servidor de Datasnap.

1. Criar um Servidor Datasnap padrão

Ao criar um Servidor DataSnap (pelo expert do Delphi), várias UNITs são adicionadas.
Se observar a UNIT que nos importa aqui é a WebModuleUnit1;

Toda vez que é feito uma chamada a um recurso na porta do HTTP do servidor e for um arquivo será executado o evento “WebModuleBeforeDispatch” o que permite inserir códigos que execute algum procedimento diferente daqueles previstos.

No nosso caso queremos chamar um arquivo externo “index.html”, “jquery.js”, “minhafoto.png”, “my.pdf”… enfim, um arquivo que esteja armazenado na pasta base do site “www”.

2. Inserindo código para suporte ao HTTP Server

Tentando facilitar o trabalho introduzi um novo componente através da  “UNIT DataSnap.HTTPModule;”


// incluir na USES
uses ... DataSnap.HTTPModule...
..

// Incluir um Private para o objeto 
   {Componente que implementa o código de resposta para o HTTP Server - por Amarildo Lacerda}
   FHttpModule:TDataSnapHTTPModule;
..

// alterar o Create para
procedure TWebModule1.WebModuleCreate(Sender: TObject);
begin
  {
     incia o componente indicando a pasta onde será hospedado a URLBase para oa arquivos de HTTP Server
     [pastaBase]/index.html
  }
  FHttpModule:=TDataSnapHTTPModule.Create(self);
  FHttpModule.URLPath := ExtractFilePath(paramStr(0)) + 'www';
  ForceDirectories(FHttpModule.URLPath);

  FServerFunctionInvokerAction := ActionByName('ServerFunctionInvokerAction');
end;

// alterar 
procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  { Redireciona a chamada HTML, JavaScript ou outro tipo de arquivo que exista na pasta padrão - por Amarildo Lacerda}
  FHttpModule.GoURLDocument(Request, Response, Handled);
  if Handled then
    exit;


  if FServerFunctionInvokerAction <> nil then
    FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
end;

Pronto… Seu Servidor Datasnap já sabe como responder como um HTTP Server nativo.

Digite: http://localhost:8080 – e o servidor irá procurar por uma página padrão: index.html

Código de Exemplo: Datasnap.HttpModule

Share Button

Trabalhar com RECORD é mais fácil administrar memória quando comparado com classes.

Como cada variável RECORD ocupa um endereço diferente com os dados e controla a sua retirada da memória com mais facilidade, prefiro usar RECORD para fazer CACHE de dados.

Por outro lado, temos vários acessos ao banco de dados que possuem parâmetros que estão nestes RECORD (alguém lembrará um CRUD). Uma solução é utilizar RTTI para ler os valores dos parâmetros que estão armazenados no RECORD e passar os valores para os parâmetros (TParams).

function TALQuery.FillParams<T>(rec: T): TALQuery;
var
  LList: TJsonValuesList;  // unit System.uJson
  LPair:TJsonPair;
  i:integer;
  prm:TParamBase;
begin
  result := self;
  LList := TJsonValuesList.Create();    // unit System.uJson
  try
    TJsonValue.GetRecordList<T>(LList, rec); // carrega os valores do RECORD em um LIST
    for I := 0 to params.count-1 do
         begin
            prm:= params[i];
            LPair := LList.names[ prm.Name ];
            if assigned(LPair) then
              case prm.DataType of
                 ftSmallint,ftInteger:
                   prm.AsInteger := LPair.JsonValue.GetValue<integer>;
                 ftFloat:
                   prm.AsFloat := LPair.JsonValue.GetValue<double>;
                 ftCurrency:
                   prm.AsCurrency := LPair.JsonValue.GetValue<Currency>;
                 ftDateTime,ftDate,ftTime :
                   prm.asDateTime := LPair.JsonValue.getValue<TDateTime>;
               else
                 prm.Value := LPair.JsonValue.getValue<string>;
              end;
         end;
  finally
    LList.free;
  end;
end;


/* aplicando */


Type
    TPedidoRecord = record
      ...
      pedido:integer;
      filial:integer;
      data:TDatetime;
    end;


var qry:   TMinhaQuery;
    rec : TPedidoRecord;
begin
    ....
    qry.fillParams<TPedidoRecord>(rec);
    ...
end;

 

Dependência: System.uJson