A versão 2 do ODataBr (antigo MVCBrODataServer) recebeu novos recursos de melhoria de performance e outros de funcionalidades como segue.

1. Melhorias
– algumas propriedades do FireDAC foram alteradas visando a melhoria de performace – alterações de propriedades;

2. Correções
– Foi feito correção para tabelas JOIN

exemplo: http://localhost:8080/OData/OData.svc/cliente(1)/vendas

no exemplo é feito um JOIN entre os dados do cliente=1 e a tabela de vendas para mostrar as vendas do daquele cliente;

3. Recursos novos

– Adicionado na pasta ./bin/admin o front-end de acesso administrativo para acesso: http://localhost:8080/admin
– Nas instruções PATCH de atualização dos dados no servidor, acrescentado a opção “rowstate”: “modifyORinsert” – tenta fazer um update, caso não encontre nenhum registro então tenta um INSERT;
com isto a propriedade “rowstate” passou a ter as seguintes opções: inserted, updated, modified e modifyORinsert
– proxy para gerar código TypeScript nativo fazendo a chamada: http://localhost:8080/OData/hello/ng irá retornar um código typescript/angular para acesso ao metadata utilizado no servidor;

exemplo: http://localhost:8080/OData/hello/ng
[code]

/// <summary>
/// ODataBr – Generate NG5 Script
/// Date: 17/01/2018 22:39:46
/// Auth: amarildo lacerda – tireideletra.com.br
/// gerado pelo Servidor ODataBr: …/OData/hello/ng
/// </summary>

import { Injectable, Inject } from ‘@angular/core’;
import { HttpClient, HttpHeaders } from ‘@angular/common/http’;
import { ODataProviderService,ODataFactory,ODataService } from ‘./odata-provider.service’;
import { Observable } from ‘rxjs/Rx’;

export interface ODataBrQuery extends ODataService{}

@Injectable()
export class ODataBrProvider {
token:string="";
urlBase:string="";
urlPort:number = 8080;
headers: HttpHeaders;

port(aPort:number):ODataBrProvider{
this.urlPort = aPort;
return this;
}
url(aUrl:string):ODataBrProvider{
this.urlBase = aUrl;
return this;
}

getJson(url:string):Observable<any>{
this.configOptions();
return this._odata.getJson(url);
}
getOData(query:ODataService):ODataProviderService{
this.configOptions();
return this._odata.getValue(query);
}
private configOptions(){
if (this.token!="") { this._odata.token = this.token; };
this._odata.createUrlBase(this.urlBase,this.urlPort);
if (this.headers.keys().length>0) {
//this._odata.headers.clear;
for(let item of this.headers.keys() ){
this._odata.headers.set(item,this.headers.get(item));
}
}
}

constructor(private _odata:ODataProviderService ) {
this.headers = new HttpHeaders();
}
get_filial( query:ODataBrQuery ):ODataProviderService {
this.configOptions();
query.resource = "filial"+(query.join?query.join:"");
return this._odata.getValue( query );
}

get_produtos( query:ODataBrQuery ):ODataProviderService {
this.configOptions();
query.resource = "produtos"+(query.join?query.join:"");
return this._odata.getValue( query );
}

}

[/code]

– Adicionado classe base em Typescript/Angular para acesso o servidor OData com a estrutura fundamental de acesso aos dados
Classe typescript/angular para acesso ao servidor

Exemplo de utilização da classe typescript/angular é possível consulta o font-end de “admin” em: http://front-end admin

ver também:
introdução ao oData

Porque um TTimer Anonymous ?

A vantagem em ter um TTimer usando método anônimo esta na facilidade em criar/destruir o objeto sem precisar criar o evento OnTimer para receber a mensagem.

[code]

TTimer.createAnonymousTimer(
function: boolean
begin
/// executa em loop enquanto retornar FALSE… para finalizar o Timer: retornar TRUE;
return := not queroContinuar();
end, 10, 500);

{
AFunc -> TFunc<Boolean>
AInterInicial -> tempo para primeira execução
AInterRegular -> intervalor para as demais execuções.
}
[/code]

Ver unit: TTimer.CreateAnonymousTimer(..)

Estava pesquisando como fazer coleta de temperatura para uma solução de Angular5 implementando com Typescript; A solução é bem simples, mas pode ajudar aqueles que estejam buscando tal solução;

Implementando a classe “provider”
[code]
/*
<summary>
subject: weather-service para acessar openweathermap
auth: amarildo lacerda <tireideletra.com.br>
uso:
– providers: [
WeatherService,..]

– para enviar pedido de temperatura:
ngOnInit() {
this._weather.getWeatherbyName("SAO PAULO,BR").subscribe(rsp => {
this.weather.fillWeather(rsp);
console.log(this.weather.response());
});
}
– …fillWeather -> preenche os dados retornados do json

</summary>

*/
import { Injectable } from ‘@angular/core’;
import { Http, Response, URLSearchParams } from ‘@angular/http’;
import { Observable } from ‘rxjs/Rx’; /// para corrigir erro do catch

export interface Coord {
lon: number;
lat: number;
}

export class Weather {
temp: any;
temp_max: any;
temp_min: any;
description: string;
city: string;
county:string;
date: any;
pressure: any;
humidity: any;
sunrise: any;
coord: Coord;
wind:any;
weather:any;
private resp: any;
response() {
return this.resp;
}
public fillWeather(body: any) {
this.resp = body;
this.city = body.name;
this.county = body.sys.country;
this.description = body.weather[0].main;
this.temp = body.main.temp.toFixed(0);
this.temp_max = body.main.temp_max;
this.temp_min = body.main.temp_min;
this.pressure = body.main.pressure;
this.humidity = body.main.humidity;
this.date = new Date(body.dt * 1000);
this.sunrise = new Date(body.sys.sunrise * 1000);
this.coord = body.coord;
this.wind = body.wind;
this.weather = body.weather;
}

}

@Injectable()
export class WeatherService {
city: string = "Sao Paulo,BR";

urlAPI = "http://api.openweathermap.org/data/2.5/weather";
tokenAPI = ""; //gerar uma API no provedor – esta é limitada
units = "metric";

constructor(private http: Http) {

}

getWeatherbyName(localName: string = "Sao Paulo,BR", api:string="") {
if (localName == "") {
localName = this.city;
}
if (api==""){
api = this.tokenAPI;
}
console.log(localName);
let myParams = new URLSearchParams();
myParams.append(‘appid’, api);
myParams.append(‘q’, localName);
myParams.append(‘units’, this.units);
return this.http.get(this.urlAPI, { search: myParams })
.map(this.extractData)
.catch(this.handleError);
}

private extractData(res: Response) {
let body = res.json();
return body;
}

private handleError(error: Response | any) {
console.error(error.message || error);
return Observable.throw(error.message || error);
}

}

[/code]

Usando o “Provider”

[code]

import { Component, OnInit } from ‘@angular/core’;
import { WeatherService, Weather } from ‘./weather-service’;

const
token = "gerar um token no site – openweathermap";
const
local = "SAO PAULO,BR";

@Component({
selector: ‘app-previsao-tempo’,
template: ‘<div>{{weather.city}}: {{weather.temp}}c </div> \
Min: {{weather.temp_min}}c Max: {{weather.temp_max}}c ‘,
styleUrls: []
})
export class PrevisaoTempoComponent implements OnInit {
weather: Weather = new Weather();
constructor(private _weather: WeatherService) {
}
ngOnInit() {
this._weather.getWeatherbyName(local,token).subscribe(rsp => {
this.weather.fillWeather(rsp);
console.log(this.weather.response());
});
}

}

[/code]

Executar um “select” em paralelo na verdade é bem simples, mas complexo se não deter algumas informações elementares sobre o tratamento que o FireDAC dá ao isolamento de conexão.

Basicamente o processo se dá pelo isolamento da “connection” ao fazer a chamada no banco de dados, talvez aí o maior problema já que é necessário ter uma conexão individual para cada chamada ao banco.

No FireDAC esta disponível na IDE o componente TFDMonitor que é responsável em gerar um novo componente de conexão para cada chamada feita. Este é o caminho mais fácil para utilizar “multithreaded”, assunto que não irei tratar aqui já que o interessante é explorar recursos que entregue mais conteúdo a quem quer entender como o processo acontece.

em construção …. ajude a escrever este texto enviando comentários. Gratidão

Base para o texto:
Unit: Data.FireDAC.Helper
Ver: Exemplo no GIT

Criar um novo componente no Delphi é basicamente implementar algum comportamento “by code” que será executado quando for requisitado ao componente.

base
Um componente publicado na paleta de objetos da IDE permitem arrastar e soltar para os formulários entregando recursos RAD ao desenvolvedor.
O componente pode ter um comportamento visual no FORM quando herda de outro componente visual, ou não visual quando herda de um TComponent;

Exemplo

  • um componente visual: TEdit
  • componente não visual: TDatasource

estrutura de um componente
[code]
/// sample1
unit xxxx;

type
/// minha implementação
TMeuComponent = class(TComponent)
private
FAbout:string;
published
/// mostrar a propriedade no Object Inspect da IDE
property About:string read FAbout write FAbout;
end;

/// registrar o component na paleta da IDE
procedure register;

implementation
procedure register;
begin
RegisterComponents(‘MVCBr’,[TMeuComponent] );
end;
end.
[/code]

Caso o componente implemente uma interface é possível indicar qual a interface junto com a mesma declaração do novo componente:

[code]
/// sample2
unit xxxx;

type
IMeuComponent = interface
[Ctrl+G para obter um GUID]
….
end;
/// minha implementação
TMeuComponent = class(TComponent,IMeuComponent)
private
FAbout:string;
published
/// mostrar a propriedade no Object Inspect da IDE
property About:string read FAbout write FAbout;
end;

/// registrar o component na paleta da IDE
procedure register;

implementation
procedure register;
begin
RegisterComponents(‘MVCBr’,[TMeuComponent] );
end;
end.
[/code]

a pegadinha

Para componentes simples será suficiente o código de exemplo indicado. Já quando o componente encapsula outros componentes (visuais/ou não) que são ligados diretamente no FORM através de propriedades, será necessário código adicional para ajudar a IDE a retirar a ligação dos componentes quando não forem mais necessário.

vejamos um exemplo de um componente com um “link”:

[code]
///sample3
type
TDBMeuComponent = class(TComponent)
private
FDatasource:TDatasource;
published
property Datasource:TDatasource read FDatasource write FDatasource;
end;
[/code]

escrevendo para a IDE

Neste caso (sample3) a propriedade Datasource permitirá que em tempo de design seja ligado um TDatasource na propriedade Datasource; A ligação do componente ocorre visualmente na IDE (ligando as propriedade), o problema ocorre neste ponto em razão da IDE não saber lidar com o desfazimento desta ligação. Como a IDE não sabe qual o momento correto em desfazer a ligação, é necessário indicar esta situação no código através do método “Notification” como segue.

[code]
///sample4
type

TDBMeuComponent = class(TComponent)
private
FDatasource: TDatasource;
protected
procedure Notification(AComponent: TComponent;
AOperation: TOperation); override;
published
property Datasource: TDatasource read FDatasource write FDatasource;
end;

{ TDBMeuComponent }

procedure TDBMeuComponent.Notification(AComponent: TComponent;
AOperation: TOperation);
begin
inherited;
/// retira o link do datasource
if AComponent.Equals(FDatasource) and (AOperation = TOperation.opRemove) then
FDatasource := nil;
end;
[/code]

Importante !!!
A maioria dos BUGs de IDE em tempo de Design ocorre pela uso inadequado do Notification, então é bom ficar atento a este ponto obrigatório para aqueles que pensam em implementar novos componentes para a IDE do Delphi.

Exemplo Completo

As units MVCBr.MongoModel e MVCBr.FDMongoDB implementam acesso as coleções MongoDB diretamente em um componente TFDMemTable abrindo os dados e enviando as alterações de volta no servidor do MongoDB.

base
O acesso ao MongoDB implementado como um Model do MVC, faz acesso direto a biblioteca MongoWire e está pronto para Auth3 presente na versão mais recente do MongoDB.
Dado a facilidade de uso do MongoDB que não exige um “schema” para as tabelas é uma boa solução em ambiente que requeira compartilhamento de dados e flexibilidade na mudança de estrutura dos documentos, justificando esforço adicional para implementar soluções RAD que de velocidade ao “coder”.

elementar
Bancos NoSQL como MongoDB não são acessados com comandos tradicionais como no ambiente SQL. É uma nova fronteira em armazenamento de dados que evoluiu muito recentemente, ainda que há um movimento para retroceder para modelos Cliente/Servidor mais tradicional adicionado de boas experiências com aprendizagem NoSQL como é o caso de TimescaleDB que faz uma mescla de recursos do Postgres e novos recursos até então visto somente nos bancos NoSQL – Novos estudos pela frente.

componentes

MongoConnection
Provê conectividade com o MongoDB.


MongoDataset
Um Dataset para obter os dados do servidor. Onde: CollectionName representa a tabela (coleção) que armazena os documentos; KeyFields é uma lista de colunas (seprado por “;” que indica a “where” a ser submetido ao servidor quando faz um “update” ou “delete”;

Exemplo no Git

Veja o Mongo Tips do amigo Thulio Bittencourt

Singleton – este é um cara engraçado. Não aceita sobrecarga nem substituição – fecha em sí e não abre para estender seu recursos. Maaasss… ele é o fino em compartilhar os seus atributos. Sempre que alguém pergunta por ele, a resposta é a sempre a mesma – inflexível e sem surpresas.

base
Singleton é uma instância global a ser evitada, mas quando não tem outra saída – é um bom parceiro.

elementar meu caro
Sendo um Singleton uma instância global, basta criar a instância uma única vez e sair usando – controlar o momento em que um Singleton pode ser liberado da memória pode ser um bom desafio. Em geral a instância é criado em algum momento do código e sua liberação ocorrerá ao finalizar a aplicação, mantendo-o carregado na memória durante o tempo de vida do aplicativo.

quando usar
Sempre que for necessário uma instância que faça controle de outras instâncias, o Singleton será bem útil. O Delphi possui alguns deles como exemplo o “Application”, uma instância que existe por toda a vida ativa do aplicativo, criado ao entrar e liberado somente quando é encerrado.
O MVCBr possui um subtitulo ao “application” o “ApplicationController”. ApplicationController é uma instância única que mantém uma pilha de “controllers” aberto no aplicativo, aqueles que estão instanciados.

limitações

Quando o desejo é atribuir um variável única para o um objeto específico e suas herança, o uso do Singleton é bastante “simples” – basca criar um “Class Var” para a instância a controlar.
Se de outro lado, se o desejo for permitir herança e cada herança tem o seu próprio Singleton então “Class Var”* não irá resolver a classe base precisa implementar mecanismos mais espertos (*a class var pertencerá sempre ao pai e não permite nova cópia aos filhos).

curiosidades
O TMVCBrSingleton faz uso de outros dois patterns:
LazyLoad – quando cria a instância para a classe alvo somente quando ela for necessária (ver invoke);
Adpater – exporta a classe base por default sem necessidade de fazer referências indiretas ( ver ..factory.mudarFlag(true) .. )

Uma classe que permite transferir um Singleton individual aos filhos
Unit: MVCBr.Patterns.Singleton
O Singleton do MVCBr implementa uma interface de adaptadora para a interface IMVCBrSingleton como já vimos antes (ver mais: princípio adaptador para interface ). Como uma classe adaptadora, é possível aplicar o “generics” para retornar o próprio singleton adaptado pela classe, vejamos:

[code]
unit singleton.MinhaClasse;

interface

uses MVCBr.Patterns.singleton;

type

/// minha classe alvo
TMinhaClasseAlvo = class(TObject)
public
function mudarFlag(AValue: Boolean): Integer;
end;

/// criando uma classe factory filha
TMinhaClasseFactory = class(TMVCBrSingleton<TMinhaClasseAlvo>)
end;

var
MinhaClasseFactory: IMVCBrSingleton<TMinhaClasseAlvo>;

implementation

{ TMinhaClasseAlvo }
function TMinhaClasseAlvo.mudarFlag(AValue: Boolean): Integer;
begin
result := ord(AValue);
end;

initialization
MinhaClasseFactory := TMinhaClasseFactory.new() ;
end.
[/code]

caso 1
Para criar uma instância local da interface – não parece lógico, somente para ilustrar, podemos fazer:
[code]
var
MinhaClasseFactory: IMVCBrSingleton<TMinhaClasseAlvo>;
begin
/// LOCAL
/// criando a classe factory para singleton
MinhaClasseFactory := TMVCBrSingleton<TMinhaClasseAlvo>.New();
showMessage(MinhaClasseFactory.mudarFlag(true).ToString);

[/code]

caso 2
Usando a classe factory que implementa uma herança de TMVCBrSingleton:
[code]
// usa o singleton
showMessage( MinhaClasseFactory.mudarFlag(false).toString);
[/code]

Um dia vi um programador tentando usar um componente VCL comum do Delphi que não tinha um correspondente DB para ele. Olhando a ginástica necessária para o componente responder a movimentações do Dataset, então lembrei quando estava aprendendo Delphi a uns 20 anos. O mais sensato é criar um novo componente DB para o mesmo.

base
Um componente DB é um componente de uso geral que responde a eventos existentes no TDatasource. O TDatasource emite uma chamada para o evento – no lado do componente, ele trata o evento e atualiza a visualização para o usuário.

elementar meu caro
Para receber evento, um componente DB deve responder aos eventos emitidos pelo TDatasource e fazer o ligação com a coluna da tabela que ao qual esta associado. Assim sendo, o componente DB irá expor uma ligação para o TDatasource (Datasource) e outro para o coluna desejada (Field);

o exemplo
Sabe aquele componente encontrados nos celulares ON/OFF, que tal criar um componente VCL DB para ele. O componente que executa esta funcionalidade na VCL é o TToggleSwitch, então vamos torná-lo um “dataware”.

usando unit: MVCBr.DataControl
Para facilitar a criação dos diversos componentes DB existentes no MVCBr – é feito uso do componente TMVCBrDataControl que entrega o mecanismo básico a todo componente DB. Ele que faz a ligação com evento “OnChange” do Datasource com a base do novo componente, vejamos como ficou…

[code]

TDBToggleSwitch = class(TToggleSwitch)
private

published
property ValueTrue: string read FValueTrue write SetValueTrue;
property ValueFalse: string read FValueFalse write SetValueFalse;
property DataField: string read GetDataField write SetDataField;
property Datasource: TDatasource read GetDatasource write SetDatasource;
end;

[/code]

as propriedades
Datasource e DataField são comuns aos componentes DB para ligar TDatasource e coluna da tabela;
ValueTrue: indicar qual o valor encontrado na tabela que indica ON;
ValueFalse: indicar qual o valor encontrado na tabela que indica OFF;

Unit completa no GIT

O uso de “Template” na IDE do Delphi proporciona melhoria de produtividade a medida que reduz digitação de rotinas repetitivas.
Ainda a pouco tempo abordei sobre o uso de Template de MVCBr e ORMBr aqui no blog – hoje vamos ver alguns “templates” disponíveis para uso com MVCBr.

base
Os “templates” de MVCBr encontram-se na pasta .\templates; A instalação dos templates é basicamente copiar os arquivos XML para a pasta padrão de templates do Delphi como já foi tratado no “artigo anterior sobre templates“.

elementar meu caro
Copiando os arquivo XML para a pasta padrão, o Delphi irá disponibilizar as macros de chamada para os templates utilizando o atalho: CTRL+J ou ainda pelo uso da sequência de palavras que identificam um template em particular.

base para acesso aos templates
mv – macros para views
mc – macros para controller
mm – macros para model
mo – macros para Observables

vamos a lista de macros para os templates (CTRL+J)

VIEWS
mv.conv – utilizado para converter um FORM já existente em um componente para MVCBr (criar interfaces para View, Controller e Classe Controller);
mv.docommand – criar método DoCommand (um override) a ser adicionado para VIEWs que queira receber notificação de EVENTS do Observer de controle interno ao framework;
mv.form – criar as assinaturas de classe e interfaces que permite converte um FORM para VIEW (mais enxuto em relação ao mv.conv);
mv.update – criar método UPDATE (um override) para views que receberam eventos UPDATE enviado pelo observer genérico de usuário;
mv.uses – criar uma lista de dependências de uses para VIEWs (auxiliar de conversão de FORMs que já existem);

Controlles
mc.addmodel – adiciona modelo de exemplo para adicionar um MODEL ao controller – utilizado no Create do Controller ou CreateModules;
mc.factory – cria a assinatura para um controller, útil para criar controllers independentes ou para conversão de código legado;
mc.fdconnection – cria uma sequência de código para o controller que gerar conexão de banco dados FireDAC;
mc.init – cria uma sequência de geração de INIT que permite inserir um TFormFactory em um controller;
mc.initialization – cria a chamada de registro do controller para a lista de controllers disponível ao aplicativo;
mc.interface – cria uma sequência para gerar uma interface para o controller;

Models
mm.creator – cria uma UNIT exemplo de geração de um MODEL genérico;
mm.ormbr.fd – cria uma sequência de integração de FireDAC Modelo do ORMBr com Model MVCBr;
mm.ormbr.sqlite – cria uma sequência de integração com SQLite deo ORMBr com Model MVCBr;
mm.ormbr.createGetTable – cria uma sequência para função GetTable para ORMBr;
mm.ormbrfb.table – cria conjunto para gerar acesso Firebird de integração de ORMBr com Model MVCBr.

Observable
mo.subscribe – criar um serviço de Observer assinando um determinado evento – como se fosse um “callback”;
mo.unsubscribe – remove o serviço do Observer;

GIT com a lista completa dos XML utilizados

Impressionante o número de vezes que precisamos copiar os valores de atributos de um objeto para outro ou simplesmente criar um novo com as mesmas propriedades.
Conhecendo a classe TMVCBrPrototype notamos o poder de RTTI combinado com Generics nos permite facilitar o trabalho sem precisar digitar todos aquelas propriedades envolvendo os dois objetos.

base
Usando Generics para passar o tipo de classe a ser manipulado e com RTTI descobrimos quais os atributos desta classe… agora é só os valores de uma para o outro.

elementar meu caro
Uso de Generics tem por finalidade facilitar o RTTI pegar quais os atributos a serem copiados do objeto alvo. Para que a cópia ocorra, o objeto de origem e de destino precisam possuir o mesmo nome para o atributo. O ideal é que ambos possuam a mesma tipagem, ainda que não seja obrigatório.

[code]
TMVCBrPrototype = Class(TInterfacedObject, IMVCBrPrototype)
public
class procedure Copy<T: Class>(ASource: T; ATarget: T); static;
class function Clone<T: Class>(ASource: T): T; static;
class function New<T: Class>: T; static;
end;

// Como copiar de B para A

var B,A : TMinhaClasse;


se: B := TMinhaClasse.create;
B.nome := ‘x’;

clone:
A := TMVCBrPrototype.Clone<TMinhaClasse>(B); /// Cria A e copia B para A

para copiar (requer que A e B já estejam criados):
TMVCBrPrototype.Copy<TMinhaClasse>(B,A); /// copia B para A

[/code]

TMVCBrPrototype