Share Button

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

Share Button
  1. Como inicializa o projeto  MVCBr ?

    R. No início no projeto (.dpr) o assistente inclui:

      
    ApplicationController.Run(TLojaAppController.New,
        function: boolean
        begin
          // retornar True se o applicatio pode ser carregado
          // False se não foi autorizado inicialização
          result := true;
        end);
    
  2. No formulário principal, como abrir uma janela com outra VIEW?

    R. todo VIEW representa um formulário e é controlado por um “controller” – o “controller” é a porta de entrada para inicializar um formulário, assim é necessário criar o “controller”:

    procedure TFormChildSampleView.Button1Click(Sender: TObject);
    var aController:IFormChildController;
    begin
       aController := resolveController<IFormChildController>;
       aController.ShowView();
    end;
    
  3. Tenho um MODEL com regras de negócio, como acessar os dados desses MODEL?

    R. O primeiro passo é tornar o MODEL visível para o “controller”, visto que o “controller” mantem uma lista de MODEL associado a ele mesmo.
    Para instânciar o MODEL dentro do “controller”:

    /// Adicionar os modulos e MODELs personalizados
    Procedure TFormChildSampleController.CreateModules;
    begin
      // adicionar os seus MODELs aqui
      // exemplo: add( MeuModel.new(self) );
    
      add( TRegrasNegociosModel.new(self)  );
    end;
    

    Na view você pode usar o “controller” para chamar o “MODEL”.

  4. Como chamar um MODEL de dentro de um VIEW?

    R. O MODEL é inicializado junto com o “controller” que mantem uma lista de MODELs ativos para acesso.

    procedure TFormChildSampleView.Button2Click(Sender: TObject);
    var
       AModel:IRegrasNegociosModel;
    begin
       AModel := GetModel<IRegrasNegociosModel>;
       AModel.Validar('Show Model Validar()');
    end;
    
  5. Quero embutir um VIEW dentro de um TABSHEET, como posso acessar os atributos do formulário ?

    R. você precisa inicialmente carregar o “controller” do VIEW – isto vai permitir acesso ao GETVIEW.This que retorna a instância do TFORM;

    procedure TFormChildSampleView.Init;
    var
       ACtrl:IEmbededFormController;
       AForm:TForm;
    begin
      // incluir incializações aqui
    
       // embeded form...
       ACtrl :=  resolveController<IEmbededFormController>;
       AForm := TForm(ACtrl.GetView.This);
       AForm.parent := TabSheet1;
       AForm.BorderStyle := bsNone;
       AForm.Align := alClient;
       AForm.Show;
    
    end;
    
    
  6. Quando trabalho com FMX é comum utilizar TABCONTROL ou TLAYOUT para mostrar VIEWs, como um VIEW pode enviar uma mensagem para outro VIEW que já esta carregada ?

    R. para enviar uma mensagem para um VIEW específico, você precisa da “interface” do VIEW desejado – o que pode ser feito incluindo no USES da UNIT uma chamada para a UNIT de “interface” do VIEW.

    procedure TFormChildSampleView.Button4Click(Sender: TObject);
    begin
       ApplicationController.ViewEvent(IEmbededFormView,'Message to: EmbededFormView');
    end;
    

    Outra forma é se você conhece o “controller” alvo, pode enviar a mensagem diretamente para o “controller”.

    procedure TFormChildSampleView.Button5Click(Sender: TObject);
    begin
      ResolveController<IEmbededFormController>.ViewEvent('Message via controller');
    end;
    

    Para mandar mensagem para todos os VIEW é possível utilizar o ApplicationController:

    procedure TFormChildSampleView.Button3Click(Sender: TObject);
    begin
       applicationController.ViewEvent( 'generic message to all VIEW' );
    end;
    

    GIT com Exemplo

Share Button

Introdução
Tomando emprestado o WIKI “plugin” é um componente computacional que adiciona recursos a um programa existente. Quando um programa suporta “plugins” ele permite ser customizado para responder a necessidades não previstas no projeto original.

Uma interface de “plugin” deve prever a possibilidade de um conjunto de código ou janela permitir ser inserida em partes do programa principal.

Em um primeiro instante – é comum encontrar “plugins” que publicam alguma função ou procedimento a ser chamado pelo aplicativo principal. Este modelo limita as funcionalidade dos “plugins” que em geral ficam mais estáticos a serem um item de menu ou uma ou outra funcionalidade.

O “plugin” que irá assinar algum serviço do aplicativo

A idéia que motiva esta publicação é a construção de um modelo de aplicativo principal HOST que publica serviços implementados em seu código e ficam disponíveis para que os “plugins” possam assinar estes serviços.

Então o HOST se torna um “publisher” é o plugin um “subscriber”, onde o plugin que solicita a assinatura de um determinado serviço do HOST.

Com a mecânica em que o “plugin” assina aos serviços do aplicativo servidor permitirá que o “plugin” tome a decisão sobre qual tipo de serviço ele quer assinar no aplicativo principal . Um exemplo é um “plugin” assinar  para ser um item do menu –  em outros casos poderá assinar funcionalidade de uma “aba” de janela ou adicionar “frames” a alguma interface do usuário – entregando mais poder de escolha ao “plugin”.

Segurança x Flexibilidade

Ainda que “flexibilidade” seja o principal objetivo, segurança segue o mesmo caminho da flexibilidade. Há que se questionar o quanto um HOST pode ser seguro o bastante para não permitir um assinante malicioso. Não tenho resposta para a questão, já que um “plugin” maldoso poderia assinar a serviços para aplicar táticas maliciosas – questão que precisa ser avaliada.

diagrama

Uma interface para publicar serviços

Considerando que o programa principal tem um formulário:

TMeuMenu = class(TForm)

end;

o que precisamos fazer é dotar o formulário principal de uma interface que permita publicar os seus serviços:

  IPluginApplication = interface
    ['{6ED989EA-E8B5-4435-A0BC-33685CFE7EEB}']
    procedure RegisterMenuItem(const AParentMenuItemName, ACaption: string; ADoExecute: IPluginMenuItem);
    procedure RegisterToolbarItem(const AParentItemName, ACaption: string;  ADoExecute: IPluginToolbarItem);
    procedure RegisterAttributeControl(const AType,ASubType: Int64;  ADoExecute: IPluginControl);
  end;

Com isto o formulário passa a implementar a interface de serviço de “plugins” da seguinte forma:

TMeuMenu = class(TForm, IPluginApplication)
….
end;

 

Registrando o Plugin para o Aplicativo Principal

Antes de carregar um “plugin” o aplicativo host precisa conhece-lo (uma lista de plugins registrados). Uma forma simplista, é guardar uma lista de “plugins” disponíveis em um arquivo INI do host. A lista é necessária para que o aplicativo possa fazer carga dos seus “plugins”. Uma vez feito a carga do plugin, estes  irão assinar os serviços a que desejam interagir com o host.

O framework utiliza a implementação de DLLs para troca de código com o aplicativo principal, publicando duas chamadas – uma para carga inicial da DLL e outra para quando o aplicativo principal esta encerrando o uso do plugin:

function LoadPlugin(AAplication: IPluginApplication): IPluginItems;
procedure UnloadPlugin;
...
exports LoadPlugin, UnloadPlugin;

Carregando o Plugin
Sabedor destas duas entradas disponíveis no “plugin”, resta escrever os métodos de carga do plugin (ver TPluginManager nos fontes):

function LoadPluginService(APlugin: string;
  AAppliction: IPluginApplication): Integer;
var
  F: function(APApplication: IPluginApplication): IPluginItems;
  H: THandle;
begin
  result := -1;
  H := LoadLibrary(PWideChar(APlugin));
  if H &gt; 0 then
  begin
    try
      @F := GetProcAddress(H, 'LoadPlugin');
      if assigned(F) then
        result := LPluginManager.FPlugins.Add(H, F(AAppliction))
      else
        raise Exception.Create('Não carregou o plugin');
    except
      FreeLibrary(H);
    end;
  end;
end;

Note que a função “LoadPlugin” do plugin espera que seja enviado um IPluginApplication e retorna uma lista de plugins existente na mesma DLL, já que uma DLL pode conter 1 ou mais plugins.

O IPluginApplication, representa a interface do HOST que publica os serviços que os plugins poderão assinar no HOST – Internamente a DLL irá montar uma lista de plugins disponíveis. Cada plugin da DLL se encarrega de assinar os serviços do HOST.

Estendendo os serviços do HOST

A interface que publica os serviços no HOST é o IPluginApplication que já possui três serviços básicos para a assinatura sendo eles:

  1. MenuItem – Assinatura para se registrar em um item de menu do HOST;
  2. ToolbarItem – Assinatura para se registrar em um item da Toolbar;
  3. AttributeControl – Uma assinatura a utilizar o plugin como um “control” ou atributo de um janela.

Caso deseje implementar outros serviços para o HOST, estendendo suas funcionalidades é possível estender a interface IPluginApplication e implementa-la no HOST:

IMyPluginApplication  =  interface(IPluginAppliction)
     procedure   RegisterXXX(....);
end;

// no host
 TMyMainMenu = class(TForm,  IMyPluginApplication )
 ...
 end;

 

Parte 2 – Construindo o Plugin

(No final o código estará disponível no GIT)
….

Share Button

Como é de conhecimento da comunidade o FireDac não tem suporte completo ao Firebird3, já que o lançamento do FB3 veio depois do lançamento do Berlin.

Quando se trabalha com Package (novidade no FB3) não é possível escolher na IDE qual o procedimento a executar no componente TFDStoredProc.
Uma forma de fazer isto é escrevendo um editor (delphi way) para auxiliar a propriedade StoredProcName…


unit Data.fireStoredProcEditor;

interface

uses
  SysUtils, Classes, DesignIntf, DesignEditors, DB;

type
  TFireStoredProcNames = class(TStringProperty)
  private
    procedure GetValues(Proc: TGetStrProc); override;

  public
    function GetAttributes: TPropertyAttributes; override;
  end;

procedure Register;

implementation

uses FireDAC.Comp.Client, FireDAC.Phys.Intf;

procedure Register;
begin
  RegisterPropertyEditor(TypeInfo(string), TFDCustomStoredProc,
    'StoredProcName', TFireStoredProcNames);
end;

{ TFireStoredProcNames }

function TFireStoredProcNames.GetAttributes: TPropertyAttributes;
begin
  result := [paValueList];
end;

procedure TFireStoredProcNames.GetValues(Proc: TGetStrProc);
var
  DB: TFDCustomStoredProc;
  qry: TFDQuery;
  eh3:boolean;
  oMetaIntf: IFDPhysConnectionMetadata;
  function iff(b:boolean;t,f:string):string;
  begin
    if b then result := t else result := f;
  end;
begin
  if (GetComponent(0).InheritsFrom(TFDCustomStoredProc)) then
  begin
    DB := TFDCustomStoredProc(GetComponent(0));
    if assigned(DB.Connection) then
    begin
      if (DB.Connection.DriverName = 'FB') then
      begin
          oMetaIntf := DB.Connection.ConnectionMetaDataIntf;
          eh3 := oMetaIntf.ServerVersion.ToString[1]='3';
          qry := TFDQuery.create(nil);
          try
            qry.Connection := DB.Connection;
            qry.SQL.Text := 'select rdb$procedure_name sName from rdb$procedures ';
            if eh3 then
               qry.SQL.Text := qry.SQL.Text+ iff(db.PackageName<>'', ' where rdb$package_name = ' + QuotedStr(DB.PackageName.ToUpper),' where rdb$package_name is null ');
            qry.Open;
            with qry do
              while eof = false do
              begin
                Proc(fieldByName('sName').asString);
                next;
              end;
          finally
            qry.Free;
          end;
      end
      else
        inherited;
    end;
  end
  else
    inherited;

end;

end.

Exemplo de uma package no FB3: DateUtils Package

Criando um Packege no Delphi para a Integração
Para integrar o novo editor é necessário criar um novo projeto Package no Delphi e incluir o código do editor.

// exemplo do projeto do Package (mínimo)
package FireEditores;
{$R *.res}
requires
  DesignIDE;
contains
  Data.fireStoredProcEditor in 'Data.fireStoredProcEditor.pas';
end.