Parte I - Apresenta!o A idia de construir este curso partiu na boa aceitao que tivemos em lanar o curso em 20 partes sobre acesso a dados no Delphi for .NET, com ADO.NET (Active Data Objects) e BDP (Borland Data Provider), que o leitor pde acompanhar nos ltimos meses aqui na ClubeDelphi. O curso tambm est disponvel na forma de E-Book. A partir deste artigo, damos incio um curso de acesso a dados usando o Delphi Win32, voltado para o ambiente client/server e multitier. Acredito que muitos de vocs desenvolvedores j possuem pelo menos uma aplicao desse tipo em produo. Que tal conhecer alguns recursos interessantes para aplicar rapidamente em suas aplicaes, fazendo otimizaes e garantindo performance?
Este o primeiro de uma srie de 30 artigos que mostrar tcnicas avanadas de uso das principais tecnologias de acesso a dados do Delphi Win32: dbExpress e DataSnap. Ao longo deste curso, voc conhecer importantes caractersticas e recursos de cada um dos componentes utilizados. Em especial, pretendo apresentar inmeras dicas e segredos de utilizao de um componente que considero ser o melhor j criado pela Borland: o ClientDataSet. Pretendo criar um curso bastante dinmico: gostaria de elaborar tpicos de acordo com a necessidade e idias de vocs, amigos leitores. Escrevam e sugiram tpicos que gostariam que fossem contemplados aqui no curso. Alm disso, no final, pretendo colocar um grande documento de FAQs com perguntas e respostas, enviadas pelos leitores que faro o curso.
Para criar os exemplos aqui demonstrados, voc pode utilizar o Delphi 6, 7 ou o Delphi 2005. Como banco de dados, utilizarei o Interbase 7.5, mas voc pode utilizar qualquer outro banco de sua preferncia, como Firebird, SQL Server, Oracle, MySQL, todos os cdigos apresentados funcionam perfeitamente com qualquer um desses SGDBs. Desejo um bom curso a todos, sugestes sero bem-vindas. Um abrao a todos e desejo sucesso nos projetos de banco de dados com Delphi
Do"n#oad Voc pode fazer download de todos os exemplos deste curso a partir do endereo cc.borland.com/cc/ccweb.exe/author?authorid=222668
dbExpress$ DataSnap e C#%entDataSet& t'(n%(as a)anadas Para mais informaes sobre acesso a dados no Delphi e tcnicas avanadas, sugiro a leitura do meu livro, "Delphi: Programao para Banco de Dados e Web, como apoio para o aprendizado das tecnologias. Na obra mostro vrias tcnicas introdutrios e avanadas de desenvolvimento com ClientDataSet, dbExpress e DataSnap (multicamadas, incluindo SOAP e COM+). Parte II - Con*e(endo os (o+ponentes Nesta primeira parte do curso de dbExpress, vamos conhecer os principais componentes da paleta dbExpress e DataSnap do Delphi, vendo suas funcionalidades e objetivos. Co+ponentes do dbExpress e DataSnap Os componentes dbExpress e DataSnap podem ser vistos na figura a seguir:
Nesta primeira parte do curso, vamos conhecer brevemente cada um dos componentes envolvidos em aplicaes dbExpress. Nos artigos seguintes, vamos detalhar cada um deles. ,%s!o -era# dos (o+ponentes SQLConnection
Esse componente responsvel pela conexo com o banco de dados.
TSQLDataset
Componente responsvel por obter dados de um servidor SQL usando cursores unidirecionais. Tambm pode executar uma procedure no servidor. Ele pode atuar tanto como uma Query, uma Table ou uma StoredProc.
TSQLQuery
Componente que fornece uma maneira de executar um comando SQL usando uma conexo dbExpress.
TSQLStoredProc
Usado para executar um procedure remoto no servidor SQL.
TSQLTable
Usado para representar uma tabela acessada atravs de uma conexo dbExpress.
TSQLMonitor
Monitora as trocas de mensagens e instrues SQL feitas entre uma aplicao cliente e um servidor SQL.
TClientDataset
Utilize TClientDataset para fornecer um mecanismo de cache para os Datasets unidirecionais. Por ser conectado a um TDatasetProvider, os dados podero ser capturados de um servidor de aplicao.
TDatasetProvider
TDatatasetProvider prov dados de um Dataset e aplica as atualizaes feitas em um TClientDataset (delta) no servidor de dados. Ele responsvel por criar os pacotes de dados que trafegam entre uma aplicao cliente e um servidor de aplicao em uma arquitetura multicamadas. Ele pode se comunicar com um servidor de aplicao por meio da interface IAppServer.
SimpleDataSet
Esse componente o conjunto de quatro componentes, e facilita a conexo rpida com banco de dados, indicado para criao de aplicaes simples e prottipos.
DCOMConnection
Efetua uma conexo com um servidor de aplicao DataSnap, do tipo DCOM, MTS ou COM+
SocketConnection
Efetua uma conexo com um servidor de aplicao DataSnap, do tipo Sockets
WebConnection
Efetua uma conexo com um servidor de aplicao DataSnap, usando o protocolo HTTP. Seu uso no mais aconselhado, sendo prefervel o uso de um SOAPConnection para conexes DataSnap atravs da Web / HTTP.
SimpleObjectBroker
Permite criar um mecanismo simples de balanceamento de carga em servidores DataSnap. Por exemplo, ele pode despachar uma conexo cliente para um segundo servidor de aplicao se o primeiro servidor estiver congestionado.
SharedConnection
Permite acesso a um DataModule "filho em um servidor de aplicao com mltiplos mdulos.
LocalConnection
Permite simular um ambiente multicamadas em um ambiente 2-tier, atravs de um mdulo compartilhado (DataModule). Com isso, ClientDataSets podem "enxergar Providers em outras units, como se fosse uma camada fsica.
ConnectionBroker
Este componente tem por finalidade abstrair (isolar) o tipo de conexo para os ClientDatasets. Se algum dia for preciso mudar o tipo de servidor, no seria necessrio reconfigurar os ClientDatasets caso se mudasse DCOMConnection para SOAPConnection, por exemplo. Parte III - Ar.u%tetura Nesta parte do curso, vamos examinar detalhes sobre a arquitetura de aplicaes dbExpress que utilizam os componentes bsicos, tpicos de uma aplicao client/server ou multicamadas: SQLConnection, SQLQuery, DataSetProvider e ClientDataSet . Veremos tambm as vantagens em se utilizar uma aplicao multicamadas. Ar.u%tetura A figura a seguir mostra as partes envolvidas em um sistema multicamadas. Na primeira camada, temos o chamado servidor de dados. Nele est localizado o banco de dados propriamente dito. O banco de dados pode ser Oracle, DB2, MySQL, Interbase ou qualquer outro suportado pelo dbExpress. Como a conexo feita via TCP/IP, voc pode usar um SO diferente do Windows, como Linux. A camada intermediria, conhecida como servidor de aplicao, contm o DataModule (tambm conhecido como RemoteDataModule ) responsvel pelo acesso a dados. Nessa camada se encontram os componentes dbExpress, como SQLConnection, SQLQuery, SQLDataSet e os DataSetProviders . Nessa camada voc tambm vai incluir as chamadas regras de negcio . Business Rules so regras impostas sobre dados, que no sero mais processadas no servidor SQL. Com isso, seu sistema consegue centralizar as regras sem depender do tipo de banco utilizado. E finalmente, a camada cliente responsvel apenas pela apresentao dos dados. Contm basicamente regras de tratamento de tela e interface. Por isso, so chamadas de thin-clients (clientes leves).
A seguir, vamos examinar as vantagens de se desenvolver aplicaes multicamadas com DataSnap e dbExpress. Vantagens de uma soluo Multitier /odu#ar%0a!o da ap#%(a!o e+ 1 (a+adas Quando desenvolvemos uma aplicao Multicamadas com DataSnap, separamos a lgica de negcio e regras de acesso a banco de dados das regras de interface de usurio. Dessa forma, vrios clientes podem compartilhar as mesmas regras, que ficam encapsuladas em uma camada de acesso comum. Observe que em uma aplicao 2 camadas h geralmente uma redundncia de regra de negcio nas estaes clientes, e uma simples mudana nessa regra requer uma nova distribuio da aplicao. Suponha que voc esteja fazendo um simples cadastro de clientes, com interface tradicional baseada em formulrios. Ento voc inclui no DataModule da aplicao uma determinada validao de dados. Se voc agora precisar construir uma verso On-Line deste cadastro, atravs de um formulrio que poder ser aberto em um browser Web, ento ser necessrio recodificar a regra anterior. Neste caso, uma camada lgica intermediria poderia centralizar todas as regras e atender tanto aplicaes clientes baseadas em formulrios, quanto aplicaes baseadas em browser. C#%entes Le)es 2T*%n-C#%ents3 Uma aplicao cliente de uma arquitetura Multitier basicamente contm cdigo de interface. Todo o processamento das solicitaes de dados ao servidor SQL so feitas na camada intermediria, de forma que um cliente nunca se comunica diretamente com o banco de dados. Dessa forma, uma aplicao cliente DataSnap muito leve, pequena, e de configurao quase zero. Por este motivo este tipo de cliente tambm conhecido como Thin-Client ou cliente leve (magro). E(ono+%a de #%(enas de a(esso a Ban(o de dados Quando trabalhamos com aplicaes 2 camadas, cada cliente de nossa aplicao se comunica diretamente com o banco de dados, por meio de um SQL Client. Este SQL Client um conjunto de bibliotecas (que muitas vezes ocupam vrios MB de espao em disco) que possibilita um terminal da rede trocar comandos SQL com um servidor de dados SQL. Voc precisa instalar estas bibliotecas em cada estao que acesse o servidor. Quando o driver do cliente troca de verso, voc precisa atualizar todas as mquinas da rede (isso sem falar da sua aplicao). Um outro aspecto nada interessante que geralmente as empresas fabricantes de banco de dados cobram licenas extras pela instalao destas bibliotecas clientes. Agora se voc construir seu sistema baseado em uma arquitetura Multitier, as bibliotecas do SQL Client (seja qual for o banco) devero ser instaladas somente na camada intermediria (camada de acesso a dados), poupando conexes no servidor e economizando licenas. Todos os clientes dessa forma compartilham uma nica conexo com o banco de dados SQL. Se seu aplicativo utiliza ainda o BDE como tecnologia Borland de acesso a dados, estes drivers tambm sero necessrio somente na aplicao intermediria, e no mais no cliente final. O mesmo vale para os drivers de acesso a dados do dbExpress. Es(a#ab%#%dade Um software bem desenvolvido sobre uma arquitetura Multicamadas extremamente escalvel. Isto , a medida que mais clientes comeam a conectar no servidor de aplicao e utilizar seus recursos, no h uma perda de desempenho e performance, como acontece em aplicaes 2 camadas tradicionais. Independ4n(%a de L%n-ua-e+ Uma camada de negcio construda sobre um padro COM, por exemplo, pode ser acessada por clientes escritos em diversas linguagens de programao que dem suporte ao COM. Essa comunicao possvel devido ao mecanismo conhecido como Interfaces. Um servidor COM (escrito em Delphi, por exemplo) pode ser acessado de um cliente escrito em VB, ASP, C, etc. Independ4n(%a de Ban(o de Dados Como uma aplicao cliente Datasnap no se comunica diretamente com o banco de dados, este no precisa nem mesmo saber qual o banco de dados utilizado. possvel migrar de Banco de Dados, por exemplo, de Interbase para Oracle, sem mesmo recompilar as aplicaes clientes (e as vezes nem mesmo a camada intermediria). Ba#an(ea+ento de Car-a Um servidor de aplicao pode ser configurado para automaticamente distribuir as conexes clientes para outros servidores de aplicao, tornando a arquitetura ainda mais escalvel. Isso facilmente alcanado apenas se usando o componente SimpleObjectBroker. 5un(%ona+ento de u+a ap#%(a!o dbExpress A figura a seguir mostra uma aplicao tpica dbExpress, e todos os passos envolvidos desde a requisio de dados at a atualizaes das alteraes no servidor. Todos os procedimentos a seguir acontecem imediatamente aps voc dar um Open em um ClientDataSet . Inclu tambm a especificao das etapas de atualizao.
Conexo: o componente SQLConnection estabelece uma conexo TCP/IP com o servidor SQL;
Abertura de um cursor de dados: um SQLDataSet ou SQLQuery efetua uma consulta ( select ) no banco de dados, por meio de um SQLConnection . O servidor SQL abre ento um cursor de dados, alocando recursos;
DataSetProvider : aps o cursor ser aberto, o DataSetProvider varre o cursor de dados (uma operao while not eof ) montando um pacote de dados ( DataPacket ).
Providing : o DataSetProvider fecha o cursor de dados, liberando recursos, e envia os dados obtidos em forma de DataPacket para o ClientDataSet , em uma operao chamada Providing .
Cache : o ClientDataSet armazena os dados em cache, local, no sendo necessria nenhuma conexo com o banco de dados;
ApplyUpdates - o ClientDataSet envia as atualizaes de volta ao DataSetProvider , em um pacote de dados conhecido como Delta ;
Resolving - para cada registro alterado, o DataSetProvider tenta atualizar o respectivo registro no banco de dados, gerando automaticamente instrues de atualizao como update, delete e insert .
Reconcile - eventuais erros ocorridos durante o Resolving so devolvidos ao ClientDataSet , para que sejam tomadas solues, em um processo chamado Reconcile . Parte I, S6LConne(t%on (r%ando (onex7es Nesta parte do curso, vamos conhecer em detalhes o componente SQLConnection e criar nossa primeira conexo dbExpress. A (#asse TS6LConne(t%on A classe TSQLConnection representa uma conexo dbExpress. Um SQLConnection faz uso de um driver para conectar um servidor de banco de dados. Os atuais bancos suportados so: DB2 MySQL SQL Server Informix Oracle InterBase
O dbExpress no suporta bancos locais, como Paradox e DBase. Isso porque o dbExpress nada mais que uma "casca fina sobre a API do banco de dados SQL. Como Paradox e DBase no so servidores SQL (SDBDs), no so suportados pelo dbExpress. Os driver e conexes so definidos em dois arquivos de configurao. O primeiro, dbxdrivers.ini em Windows ou os dbxdrivers em Linux, lista os drivers instalados e as bibliotecas (DLLs ou shared objects no Linux (so)) requeridos pela conexo. O segundo, dbxconnections.ini, em Windows ou os dbxconnections em Linux, lista as configuraes da conexo. Cada configurao representa um conjunto de parmetros de componentes TSQLConnection e descreve uma conexo com um banco de dados. Para criar uma configurao, basta dar um duplo clique em um componente TSQLConnection e usar editor para criar a conexo. S6LConne(t%on - Pr%n(%pa%s Propr%edades ActiveStatements Nmero de comandos ativos sendo executados no banco de dados
AutoClone Especifica se o componente automaticamente clona conexes com o banco de dados quando for necessrio
Connected Indica se a conexo est ativa
ConnectionName Nome da conexo no arquivo de configurao
ConnectionState Indica o corrente estado da conexo
DataSets Lista todos os DataSets ativos da conexo
DriverName Indica o nome do driver associado conexo
GetDriverFunc Indica a funo exportada na DLL do driver
InTransaction Se uma transao est em progresso
KeepConnection Indica se a conexo deve ficar ativa se no existirem DataSets abertos
LibraryName Nome da DLL do driver dbExpress
LoadParamsOnConnect Indica se o componente deve ler as configuraes dinamicamente a partir do dbxconnections.ini
LocaleCode Indica se a ordenao em DataSets deve ser com base na localizao
MaxStmtsPerConn Indica o nmero mximo de comandos ativos por conexo
MetaData Permite acesso aos metadados do banco de dados
MultipleTransactionsSupported Indica se o banco de dados suporta mltiplas transaes
Params Lista os parmetros de conexo
ParamsLoaded Indica se os parmetros foram lidos do dbxconnections.ini
SQLConnection Acesso ao objeto interno do driver dbExpress que representa a conexo
SQLHourGlass Indica se o cursor de tela (SQL) deve ser usado durante processamentos
TableScope Indica quais tipos de tabelas devem ser retornadas em operaes de metadados
TransactionsSupported Indica se o banco de dados suporta transaes
VendorLib Indica o nome da biblioteca cliente (DLL) do banco de dados S6LConne(t%on - Pr%n(%pa%s /'todos CloneConnection Retorna uma cpia do objeto de conexo
CloseDataSets Fecha todos os DataSets associados conexo
Commit Efetua um Commit em uma transao
Create Cria uma instncia da classe TSQLConnection
Destroy Destri a instncia do objeto
Execute Executa um comando SQL no banco de dados
ExecuteDirect Executa um comando SQL no banco de dados, que no possua parmetros
GetDefaultSchemaName Retorna o default schema do objeto do banco de dados
GetFieldNames Obtm a lista de campos de uma tabela, em um TStrings
GetIndexNames Obtm a lista de ndices de uma tabela, em um TStrings
GetLoginUsername Retorna o nome do usurio logado no BD
GetPackageNames Obtm a lista de packages do BD, em um TStrings
GetProcedureNames Obtm a lista de stored procedures do BD, em um TStrings
GetProcedureParams Obtm a lista de parmetros de stored procedures do BD, em um TStrings
GetSchemaNames Obtm o nome de todos os objetos do BD, em um TStrings
GetTableNames Obtm a lista de tabelas do BD, em um TStrings
LoadParamsFromIniFile Carrega os parmetros de conexo a partir de um arquivo INI
Rollback Efetua um RollBack em uma transao
SetTraceCallbackEvent Define uma funo de callback que chamada para cada comando executado no servidor
StartTransaction Inicia uma transao
Close Fecha a conexo
Open Abre a conexo S6LConne(t%on - Pr%n(%pa%s E)entos OnLogin Permite definir parmetros de login (username e senha).
TraceCallbackEvent Permite acesso a uma funo de callback que chamada para cada comando executado no servidor
AfterConnect Disparado aps a conexo ser estabelecida
BeforeConnect Disparado antes da conexo ser estabelecida
AfterDisconnect Disparado aps a conexo ser fechada
BeforeDisconnect Disparado antes da conexo ser fechada Cr%ando u+a (onex!o muito simples criar uma conexo dbExpress. Pressupondo que voc j tenha o Interbase rodando e previamente configurando (neste curso vou usar o IB 7.5), basta colocar um SQLConnection no form, dar um duplo clique sobre ele e acessar o editor de conexes:
No editor de conexes do dbExpress, clique no cone +. Escolha o driver do banco desejado e d um nome para a conexo:
Informe no parmetro DataBase o caminho do banco de dados:
Nota& O parmetro Database utilizado de diferentes formas, dependendo do driver. Para conexes IB / FB, preciso especificar o nome do host (servidor) juntamente com o caminho do banco, separados por ":. errado tentar acessar o servidor usando algo do tipo "\\servidor\c$\caminho\db.gdb. Nesse caso, o servidor seria a mquina local, mas acessando um arquivo em outra mquina usando compartilhamento de rede.
Para o SQL Server, por exemplo, o nome do servidor e do banco so indicados em parmetros distintos (HostName e Database). Nesse caso, Database no o caminho fsico do banco no disco, mas o nome registrado no Enterprise Manager. Para o DB2, o Database deve indicar o Alias configurado no DB2 Client, que contm outras informaes de acesso, como o endereo do servidor, porta etc. Procedimento semelhante tambm feito para o Oracle. Essa configurao fica salva no arquivo dbxconnections.ini dbx(onne(t%ons8%n% O dbExpress e o BDE so bastante semelhantes com relao aos parmetros utilizados em uma conexo. Como voc deve lembrar, o BDE guardava informaes sobre os aliases em um arquivo chamado idapi32.cfg, localizado normalmente em c:\Arquivos de Programas\Arquivos Comuns\Borland Shared\BDE. Esse arquivo no era manipulado diretamente, e sim utilizando- se o BDE Administrator a partir do Painel de Controle.
Com isso, ficava muito simples alterar dinamicamente um parmetro de conexo, como o caminho do banco ou endereo do servidor, pois essas informaes ficavam externas aplicao. No era preciso recompilar nada caso fosse necessrio fazer alguma modificao nos parmetros de acesso. Tambm era possvel utilizar um componente Database. Em compensao, o BDE uma "camada pesada de acesso a servidores SQL. Foi feito para o mundo duas camadas e possui um mecanismo de cache rudimentar. Freqentemente o desenvolvedor sentia-se obrigado a instalar quase 18 MB de DLLs para acesso a um BD.
Ao contrrio, o dbExpress um engine leve de acesso, baseado na implementao de interfaces que acessam diretamente o driver cliente do BD, dispensado a instalao de bibliotecas adicionais. Tudo o que voc precisa distribuir a DLL indicada na propriedade LibraryName do SQLConnection, cujo tamanho varia entre 90 kb e 200 kb, dependendo do driver. Tambm necessrio distribuir a biblioteca Midas.dll, que na atual verso possui somente cerca de 290 Kb.
Semelhante ao BDE, as informaes sobre as conexes criadas no dbExpress ficam em um arquivo de configurao, chamado dbxconnections.ini, localizado normalmente em c:\Arquivos de Programas\Arquivos Comuns\Borland Shared\dbExpress. Abra esse arquivo e veja que ele contm uma sesso chamada "DB_IB, que define os parmetros da conexo que criamos no Delphi. Veja um trecho do arquivo a seguir (esses parmetros so os mesmos que esto atualmente na propriedade Params do SQLConnection):
O arquivo dbxconnections.ini utilizado, at agora, apenas pela IDE do Delphi. Repare que cada conexo tem um parmetro chamado DriverName. O Delphi utiliza esse valor para configurar algumas propriedades do SQLConnection que so especficas do driver utilizado.
Por exemplo, se o DriverName for Interbase, as propriedades GetDriverFunc, LibraryName e VendorLib tero os valores getSQLDriverINTERBASE, dbexpint.dll e gds32.dll, respectivamente. Esses valores mudam para o DB2, Oracle, SQL Server etc. Essas configuraes so obtidas atravs de um segundo arquivo de configurao, chamado dbxdrivers.ini.
D%(a& Para mais informaes sobre os arquivos de inicializao do dbExpress, consulte os tpicos "dbxconnections.ini e "dbxdrivers.ini na ajuda do Delphi. Testando a (onex!o Pronto, agora j podemos testar a conexo! Para isso, coloquei um Button no form e digitei:
procedure &'orm(.B)tton(*lic+!ender: &,b-ect./ begin try try !"#*onnection(.,0en+./ !how1essage+2*one34o 5eita com s)cesso62./ except !how1essage+27rro ao conectar2./ end/ finally if !"#*onnection(.*onnected then !"#*onnection(.*lose+./ end/ end/
Lembre-se de definir o LoginPrompt como False. Execute e teste a aplicao.
Parte , Introdu!o ao uso de DataSets Un%d%re(%ona%s Neste artigo teremos uma pequena introduo ao conceito e uso de DataSets Unidirecionais do dbExpress, atravs dos componentes TSQLDataSet, TSQLTable e TSQLQuery. O .ue s!o DataSets un%d%re(%ona%s A principal funo dos Datasets unidirecionais recuperar dados de um banco SQL. Eles no mantm nenhum buffer na memria ou criam qualquer tipo de cache, o que era comum em Datasets bidirecionais, como os usados no BDE. Por serem implementados desta forma, Datasets unidirecionais so bem mais rpidos, exigem pouco processamento e utilizam o mnimo de recursos da mquina, diferentemente dos Datasets TQuery e TTable baseados no BDE. Em Datasets unidirecionais no permitido: edio, campos lookup, filtros e navegao com prior e last. Ao tentar realizar operaes no permitidas sobre um Dataset unidirecional, uma exceo do tipo EdataBaseError levantada com a mensagem : "Operation not allowed on a unidirectional dataset.
At o BDE, quando o usurio "rolava o cursor de dados na aplicao cliente, por exemplo, usando um DBNavigator, havia uma troca de mensagens com o "kernel do BDE, que precisava manter o cursor alocado no servidor de banco de dados para permitir a navegao bidirecional. Isso poderia comprometer a performance de solues baseadas nessa arquitetura, visto que para atender n cliente simultaneamente, o BD precisava manter ativo os cursores alocados no servidor.
Com o dbExpress, o tempo de transao e vida til do cursor de dados no servidor bastante pequeno, devido ao uso de DataSetProviders e ClientDataSets. Quando damos um Open em um ClientDataSet, enviada uma solicitao para o DataSetProvider, que abre o Dataset unidirecional associado. O DataSetProvider "varre ento o cursor aberto pela consulta e "empacota os dados em um DataPacket, que alocado na memria do ClientDataSet. Nesse momento, o usurio pode trabalhar tranquilamente nos dados em tela, e o dbExpress pode fechar a consulta (cursor) que no ficar mais ativa (diferente do BDE). Por esse motivo, aplicaes dbExpress e DataSnap so extremamente rpidas. DataSets do dbExpress Os componentes da guia dbExpress possuem os mesmos ancestrais dos componentes baseados no BDE. Isso significa que muita coisa ser semelhante ao migrar de BDE para DBX, graas aos recursos de abstrao e polimorfismo, principalmente das classes TDataset, TField e TCustomConnection. Essas classes formam a base para os vrios componentes de acesso a dados, campos e conexo a bancos de dados disponveis no Delphi.
Os DataSets do pacote dbExpress so chamados unidirecionais. Basicamente, este tipo de Dataset tem a funo de retornar dados de um servidor SQL, mas no de manipul-los (buffering). Para cada banco de dados que vamos acessar, o dbExpress fornece um driver especfico que deve ser distribudo juntamente com a aplicao. Todos os TDatsets usados no dbExpress herdam de TCustomSQLDataset. Todas as classes do dbExpress esto declaradas na unit SqlExpr.pas. Exe+p#o usando so+ente DataSets Un%d%re(%ona%s possvel utilizar DataSets Unidirecionais diretamente, sem usar um ClientDataSet. Essa tcnica bastante utilizada, por exemplo, para confeco de relatrios. Ou seja, lemos um registro, fazemos alguma coisa com ele, e navegamos para o prximo, sem armazenar nada em memria. exatamente isso que mostrarei neste exemplo. Inicie uma nova aplicao VCL no Delphi.
Coloque um SQLConnection e um SQLDataSet no formulrio.
No SQLConnection, configure uma conexo para o banco Employee do Interbase ou do Firebird (j discutimos conexes anteriormente, de forma que no vou entrar em detalhes aqui).
Aponte o SQLDataSet para o SQLConnection, atravs da propriedade Connection e em CommandText digite
select 8 5rom c)stomer
Coloque um DataSource e aponte sua propriedade DataSet para o SQLDataSet. Neste momento, voc dever receber a seguinte mensagem de erro:
Isso acontece pois um DBGrid exige navegao bidirecional no cursor de dados, o que no suportado pelo dbExpress. Para isso, voc precisaria de um ClientDataSet (no usaremos ainda neste exemplo). Retire ento o DBGrid e coloque um ListView.
No evento OnShow do formulrio digite:
procedure &'orm(.'orm!how+!ender: &,b-ect./ var it: &#istItem/ i: integer/ begin #ist9iew(.9iew!tyle := vs:e0ort/ !"#Data!et(.,0en/ try for i := ; to 0red+!"#Data!et(.'ields.*o)nt. do with #ist9iew(.*ol)mns.<dd do *a0tion := !"#Data!et(.'ields[i].'ieldName/ while not !"#Data!et(.7o5 do begin it := #ist9iew(.Items.<dd/ it.*a0tion := !"#Data!et(.'ields[;].<s!tring/ for i := ( to 0red+!"#Data!et(.'ields.*o)nt. do it.!)bItems.<00end+!"#Data!et(.'ields[i].<s!tring./ !"#Data!et(.Ne3t/ end/ finally !"#Data!et(.*lose/ end/ end/
Aqui no estamos usando nenhuma espcie de cache, fazendo uma navegao otimizada e unidirecional. Para cada registro do SQLDataSet, adicionamos os valores dos campos no ListView e a seguir navegamos para o prximo registro. A figura a seguir mostra o exemplo em execuo:
IS6LCursor Quando usamos DataSets do dbExpress diretamente, na verdade estamos trabalhando diretamente com o driver a ele associado. Essa "interface dbExpress com o banco de dados feito atravs de drivers e interfaces, definidas na unit DBXpress.pas. Veja a seguir a definio da interface responsvel pela manipulao de cursores (se voc observar, ver que usamos alguns mtodos dessa interface no exemplo anterior):
I!"#*)rsor = interface function !et,0tion+e,0tion: &!"#*)rsor,0tion/ Pro09al)e: #ongInt.: !"#:es)lt/ stdcall/ function =et,0tion+e,0tion: &!"#*)rsor,0tion/ Pro09al)e: Pointer/ 1a3#ength: !mallInt/ out #ength: !mallInt.: !"#:es)lt/ stdcall/ function get7rror1essage+7rror: P*har.: !"#:es)lt/ overload/ stdcall/ function get7rror1essage#en+out 7rror#en: !mallInt.: !"#:es)lt/ stdcall/ function get*ol)mn*o)nt+var 0*ol)mns: >ord.: !"#:es)lt/ stdcall/ function get*ol)mnName#ength+ *ol)mnN)mber: >ord/ var 0#en: >ord.: !"#:es)lt/ stdcall/ function get*ol)mnName+*ol)mnN)mber: >ord/ 0*ol)mnName: P*har.: !"#:es)lt/ stdcall/ function get*ol)mn&y0e+*ol)mnN)mber: >ord/ var 0)&y0e: >ord/ var 0)!)b&y0e: >ord.: !"#:es)lt/ stdcall/ function get*ol)mn#ength+*ol)mnN)mber: >ord/ var 0#ength: #ong>ord.: !"#:es)lt/ stdcall/ function get*ol)mnPrecision+*ol)mnN)mber: >ord/ var 0iPrecision: !mallInt.: !"#:es)lt/ stdcall/ function get*ol)mn!cale+*ol)mnN)mber: >ord/ var 0i!cale: !mallInt.: !"#:es)lt/ stdcall/ function isN)llable+*ol)mnN)mber: >ord/ var N)llable: #ongBool.: !"#:es)lt/ stdcall/ function is<)toIncrement+*ol)mnN)mber: >ord/ var <)toIncr: #ongBool.: !"#:es)lt/ stdcall/ function is:ead,nly+*ol)mnN)mber: >ord/ var :ead,nly: #ongBool.: !"#:es)lt/ stdcall/ function is!earchable+*ol)mnN)mber: >ord/ var !earchable: #ongBool.: !"#:es)lt/ stdcall/ function isBlob!i?e73act+*ol)mnN)mber: >ord/ var Is73act: #ongBool.: !"#:es)lt/ stdcall/ function ne3t: !"#:es)lt/ stdcall/ function get!tring+*ol)mnN)mber: >ord/ 9al)e: Pointer/ var IsBlan: #ongBool.: !"#:es)lt/ stdcall/ function get!hort+*ol)mnN)mber: >ord/ 9al)e: Pointer/ var IsBlan: #ongBool.: !"#:es)lt/ stdcall/ function get#ong+*ol)mnN)mber: >ord/ 9al)e: Pointer/ var IsBlan: #ongBool.: !"#:es)lt/ stdcall/ function getDo)ble+*ol)mnN)mber: >ord/ 9al)e: Pointer/ var IsBlan: #ongBool.: !"#:es)lt/ stdcall/ function getBcd+*ol)mnN)mber: >ord/ 9al)e: Pointer/ var IsBlan: #ongBool.: !"#:es)lt/ stdcall/ function get&ime!tam0+*ol)mnN)mber: >ord/ 9al)e: Pointer/ var IsBlan: #ongBool.: !"#:es)lt/ stdcall/ function get&ime+*ol)mnN)mber: >ord/ 9al)e: Pointer/ var IsBlan: #ongBool.: !"#:es)lt/ stdcall/ function getDate+*ol)mnN)mber: >ord/ 9al)e: Pointer/ var IsBlan: #ongBool.: !"#:es)lt/ stdcall/ function getBytes+*ol)mnN)mber: >ord/ 9al)e: Pointer/ var IsBlan: #ongBool.: !"#:es)lt/ stdcall/ function getBlob!i?e+*ol)mnN)mber: >ord/ var #ength: #ong>ord/ var IsBlan: #ongBool.: !"#:es)lt/ stdcall/ function getBlob+*ol)mnN)mber: >ord/ 9al)e: Pointer/ var IsBlan: #ongBool/ #ength: #ong>ord.: !"#:es)lt/ stdcall/ end/
Essa interface, juntamente com ISQLCommand, ISQLConnection e ISQLDriver compem a arquitetura aberta do dbExpress. Se um desenvolvedor quiser criar um driver dbExpress para acessar um banco de dados no suportado nativamente, deve implementar essas interfaces. TCusto+S6LDataSet A seguir, veremos as principais propriedades de TCustomSQLDataSet, que a classe base para todos os DataSets Unidirecionais do dbExpress. No listarei os membros herdados de classes mais altas, como TDataSet, focando nos membros introduzidos pela classe TCustomSQLDataSet:
BlobBuffer Reserva buffer de memria para armazenar campos BLOB. CommandText Especifica o comando que o DataSet ir executar. CommandType Indica o tipo de comando passado no CommandText, que pode ser texto, o nome de uma tabela ou nome de uma stored procedure. CurrentBlobSize Tamanho do ultimo campo BLOB lido. DataLink Identifica o Datalink que gerencia a comunicao entre o DataSet e o DataSetMaster. DataSource Faz um link ente o DataSet e outro DataSet Master. DesignerData Armazena dados customizados. GetMetadata Especifica se o DataSet obtm metadatas do BD. IndexDefs Contm definies de todos os ndices definidos para o DataSet InternalConnection Indica o componente que conecta o DataSet ao BD. LastError Indica o ultimo erro SQL retornado pelo dbExpress. MaxBlobSize Indica o nmero mximo de bytes retornados por campos BLOB. NativeCommand Representa o comando SQL que foi enviado ao servidor SQL. NumericMapping Configurao de mapeamento entre campos BCD. ParamCheck Especifica se a lista de parmetros para o Dataset reconfigurada quando a consulta muda. ParamCount Indica o nmero de parmetros do DataSet. Params Parmetros da consulta SQL. Prepared Indica se o comando est preparado para execuo. ProcParams Descrio dos parmetros de Stored Procedures. RecordCount Indica o nmero de registros obtidos pela consulta do DataSet. RowsAffected Indica o nmero de registros afetados pela execuo do ltimo comando no DataSet. SchemaInfo MetaDados do DataSet. SortFieldNames Ordem dos dados da consulta quando o CommandType for ctTable. SQLConnection Componente de conexo. TransactionLevel Nvel de isolamento de transao.
Veja a seguir os principais mtodos do componente:
CreateBlobStream Cria uma Stream para campos BLOB. GetBlobFieldData Recupera o valor corrente do campo BLOB no buffer. GetDetailLinkFields Lista os campos da relao Master / Detail. GetFieldData Recupera o valor corrente de um campo no buffer. GetKeyFieldNames Preenche uma lista com os nomes de todos os ndices do DataSet. GetQuoteChar Retorna o character usado em comandos SQL para manipular abertura e fechamento de strings. IsSequenced Indica se o DataSet pode usar nmeros de registros para indicar sua ordem. Locate Para busca de registros no DataSet, com limitaes imposta pelos cursores unidirecionais. Lookup Para busca de campos Lookup no DataSet, com limitaes imposta pelos cursores unidirecionais. ParamByName Captura um parmetro pelo nome. PrepareStatement Prepara a execuo do comando SQL. SetSchemaInfo Indica se o Dataset representa metadados do servidor e de que tipo.
Veja a seguir os principais eventos do componente: ParseDeleteSql Ocorre quando a aplicao prepara para processar um comando DELETE armazenado na propriedade CommandText. ParseInsertSql Ocorre quando a aplicao prepara para processar um comando INSET armazenado na propriedade CommandText. ParseSelectSql Ocorre quando a aplicao prepara para processar um comando SELECT armazenado na propriedade CommandText. Parse!pdateSql Ocorre quando a aplicao prepara para processar um comando !PD"TE armazenado na propriedade CommandText.
Parte ,I Re(uperando /etaDados
Nesta parte do curso, veremos como utilizar o dbExpress para recuperar informaes sobre objetos do banco de dados, como nome de tabelas, campos, ndices (o que chamamos de metadados). dbExpress e /etaDados No dbExpress, a interface responsvel pela obteno de metadados a ISQLMetaData, declarada na unit DBXpress.pas da seguinte forma:
I!"#1etaData = interface function !et,0tion+eD,0tion: &!"#1etaData,0tion/ Pro09al)e: #ongInt.: !"#:es)lt/ stdcall/ function =et,0tion+eD,0tion: &!"#1etaData,0tion/ Pro09al)e: Pointer/ 1a3#ength: !mallInt/ o)t #ength: !mallInt.: !"#:es)lt/ stdcall/ function get,b-ect#ist+e,b-&y0e: &!"#,b-ect&y0e/ out *)rsor: I!"#*)rsor.: !"#:es)lt/ stdcall/ function get&ables+&ableName: P*har/ &able&y0e: #ong>ord/ out *)rsor: I!"#*)rsor.: !"#:es)lt/ stdcall/ function getProced)res+Proced)reName: P*har/ Proc&y0e: #ong>ord/ out *)rsor: I!"#*)rsor.: !"#:es)lt/ stdcall/ function get*ol)mns+&ableName: P*har/ *ol)mnName: P*har/ *ol&y0e: #ong>ord/ ,)t *)rsor: I!"#*)rsor.: !"#:es)lt/ stdcall/ function getProced)reParams+ProcName: P*har/ ParamName: P*har/ out *)rsor: I!"#*)rsor.: !"#:es)lt/ stdcall/ function getIndices+&ableName: P*har/ Inde3&y0e: #ong>ord/ out *)rsor: I!"#*)rsor.: !"#:es)lt/ stdcall/ function get7rror1essage+7rror: P*har.: !"#:es)lt/ overload/ stdcall/ function get7rror1essage#en+o)t 7rror#en: !mallInt.: !"#:es)lt/ stdcall/ end/
Como voc pode notar pelos mtodos, podemos recuperar praticamente qualquer informao do catlogo do BD, como nomes de tabelas, tipos e nomes de campos, informaes sobre Stored Procedures e mais. Para usar essa interface, devemos usar o mtodo SetSchemaInfo de um DataSet do dbExpress. Ap#%(a!o usando /etaDados Inicie uma nova aplicao VCL no Delphi.
Figura 1. Coloque SQLConnection no e configure uma conexo para o banco Employee do Interbase ou do Firebird (j discutimos conexes anteriormente, de forma que no vou entrar em detalhes aqui).
Figura 2. Coloque mais alguns componentes dbExpress e da paleta Data Access, conforme mostrado a seguir:
Figura 3. Configure o relacionamento entre os componentes da seguinte forma:
obect DB!rid"# $DB!rid DataSo%rce & DataSo%rce" end obect DB!rid'# $DB!rid DataSo%rce & DataSo%rce' end obect SQLQ%ery"# $SQLQ%ery SQLConnection & SQLConnection" end obect DataSet(ro)ider"# $DataSet(ro)ider DataSet & SQLQ%ery" end obect ClientDataSet"# $ClientDataSet (ro)ider*ame & +DataSet(ro)ider"+ end obect DataSo%rce"# $DataSo%rce DataSet & ClientDataSet" end obect SQLQ%ery'# $SQLQ%ery SQLConnection & SQLConnection" end obect DataSet(ro)ider'# $DataSet(ro)ider DataSet & SQLQ%ery' end obect ClientDataSet'# $ClientDataSet (ro)ider*ame & +DataSet(ro)ider'+ end obect DataSo%rce'# $DataSo%rce DataSet & ClientDataSet' end No evento ,nCreate do formulrio digite o seguinte:
procedure &'orm(.'orm*reate+!ender: &,b-ect./ begin !"#")ery(.!et!chemaIn5o+st&ables@22@22./ *lientData!et(.,0en/ end/
E no e#ento OnDateChange do DataSource1 di$ite o se$uinte%
procedure &'orm(.Data!o)rce(Data*hange+!ender: &,b-ect/ 'ield: &'ield./ var tb: string/ begin *lientData!etA.*lose/ tb := *lientData!et(.'ieldByName+2&<B#7_N<172..<s!tring/ !"#")eryA.!et!chemaIn5o+st*ol)mns@tb@22./ *lientData!etA.,0en/ end/
Com isso, exibimos no primeiro DB!rid as tabelas do banco de dados. Quando uma tabela for selecionada, exibimos informaes sobre suas colunas no segundo DB!rid. Execute a aplicao:
Figura 4. Parte ,II 9nd%(es e+ +e+:r%a
A partir desta parte do curso, vamos conhecer algumas tcnicas avanadas de desenvolvimento com dbExpress e DataSnap, principalmente atravs do componente ClientDataSet. O exemplo apresentado neste artigo mostra como definir ndices em memria para o ClientDataSet. Para isso, basta setar a propriedade Inde-Field*ames (para criar ndices mais personalizados use a propriedade Inde-Defs e Inde-*ame). No necessrio criar arquivos de ndices como no Paradox ou refazer a consulta SQL no banco. Inicie uma nova aplicao Delphi VCL e coloque no formulrio principal coloque um ComboBo-, Label, ClientDataSet, DataSo%rce e DB!rid, fazendo as ligaes como mostrado abaixo:
object *lientData!et(: &*lientData!et end
object Data!o)rce(: &Data!o)rce Data!et = *lientData!et( end
object DB=rid(: &DB=rid Data!o)rce = Data!o)rce( end
D um clique de direita no ClientDataSet, escolha Load from MyBase $able e abra o arquivo C%stomer.XML. localizado nos demos do Delphi, por padro no diretrio:
*:\<rB)ivos de 0rogramas\<rB)ivos com)ns\Borland !hared\Data
Seu formulrio deve estar semelhante ao mostrado a seguir:
Figura 1. No evento ,nCreate do formulrio digite:
procedure &'rm1ain.'orm*reate+!ender: &,b-ect./ begin *lientData!et(.=et'ieldNames+*omboBo3(.Items./ end/
Isso preenche o Combo com a lista de campos disponveis no ClientDataSet.
Figura 2. No evento ,nChan/e do Combo, ordenamos o ClientDataSet atravs do campo que o usurio selecionou:
procedure &'rm1ain.*omboBo3(*hange+!ender: &,b-ect./ begin *lientData!et(.Inde3'ieldNames := *omboBo3(.&e3t/ end/
Fazemos isso tambm no ,n$ileClic0 do DB!rid, para ordenar o DataSet conforme a coluna clicada no grid:
procedure &'rm1ain.DB=rid(&itle*lic+*ol)mn: &*ol)mn./ begin if <ssigned+,ld*ol)mn. then ,ld*ol)mn.&itle.*olor := DB=rid(.'i3ed*olor/ *lientData!et(.Inde3'ieldNames := *ol)mn.'ieldName/ *ol)mn.&itle.*olor := C;;D;E;E;/ ,ld*ol)mn := *ol)mn/ end/
A figura a seguir mostra a aplicao em execuo, observe o efeito de cor que colocamos na coluna:
Figura 3. Parte ,III DataSet5%e#ds
Este exemplo mostra como utilizar DataSetFields em ClientDataSets. DataSetField um campo $Field especial que pode representar o contedo de outro DataSet. Configure uma conexo dbExpress para o banco EMPLOYEE do Interbase. Coloque um SQLConnection apontando para essa conexo. Coloque tambm duas SQLQ%ery, um DataSet(ro)ider, um ClientDataSet, dois DataSo%rces, um DB!rid, um B%tton e um DB*a)i/ator. Relacione os componentes conforme mostrado a seguir:
object Data!o)rceA: &Data!o)rce Data!et = *lientData!et( end
Seu formulrio deve estar semelhante ao mostrado a seguir:
Figura 1. Configura a instruo SQL da primeira Q%ery para:
select 8 5rom *U!&,17:
E da segunda para:
select 8 5rom !<#7! where *U!&_N,=:*U!&_N,
Configure o parmetro (propriedade (arams) da segunda Q%ery como mostrado a seguir:
Figura 2. Adicione todos os $Field no ClientDataSet, e observe que teremos um DataSetField:
Figura 3. Esse campo representa o DataSet detalhe. Com isso temos no mesmo DataSet acesso a tabela principal (master) e tabela detalhe (detail). No evento ,nCreate do form digite:
procedure &'rm1ain.'orm*reate+!ender: &,b-ect./ begin *lientData!et(.,0en/ end/
No evento ,nClic0 do boto digite:
procedure &'rm1ain.BitBtn(*lic+!ender: &,b-ect./ begin *lientData!et(.<00lyU0dates+;./ end/
A figura a seguir mostra a aplicao em execuo.
Figura 4. Navegue at a ltima coluna do DB!rid e observe que ela lista o DataSetField. Clique em [...] e note que uma outra DB!rid ser aberta automaticamente, mostrando dados da tabela detalhe, relacionados com o registro selecionado no DB!rid principal.
Figura 5. Note que usamos para isso somente u+ ;n%(o C#%entDataSet. Alm disso, alteraes no DataSetField sero refletidos no DataSet principal (por isso s precisamos de um Apply1pdates, que aplicar alteraes em ambas as tabelas no banco). Parte I< Interna#Ca#(
Este exemplo mostra como utilizar o recurso de InternalCalc em ClientDataSets, utilizado para otimizar campos calculados em memria. Faremos um exemplo que vai compara campos calculados tradicionais com InternalCalcs. O evento ,nCalcFields de um DataSet uma "armadilha. Ele , obviamente, utilizado para implementar campos calculados. O que poucos sabem que esse evento chamado a todo o momento, por exemplo, quando um valor de um campo muda, mesmo que esse campo no afete o valor do clculo. Se voc escrever um cdigo mais complexo nesse evento, como uma consulta ao banco de dados para obter um valor a ser usado no clculo (o que considero um "suicdio), ver que a performance da sua aplicao cair consideravelmente. A soluo criar o campo como InternalCalc ao invs de Calc%lated. A seguir, no evento ,nCalcFields, testamos se o estado (State) do DataSet dsInternalCalc antes de fazermos o processamento, como no exemplo:
if *lientData!et(.!tate = dsInternal*alc then *lientData!et(*<1P,.9al)e := ...
Com isso, o cdigo ser executado uma nica vez para cada registro, por exemplo quando h uma navegao ou quando um (ost chamado. Para ver um exemplo prtico, coloque um ClientDataSet no formulrio e um DataSo%rce. D um duplo clique no componente e clique de direita no editor, escolhendo *e2 Field (vamos criar um DataSet de memria neste exemplo). Adicione o campo NUM1 como mostrado a seguir:
Figura 1. Adicione o campo NUM2 como mostrado a seguir:
Figura 2. Adicione o campo DUMMY como mostrado a seguir:
Figura 3. Adicione o campo CALCULATED como mostrado a seguir (observe que ele ser um campo calculado):
Figura 4. Adicione o campo INTERNALCALC como mostrado a seguir (observe que ele ser um campo InternalCalc):
Figura 5. Arraste os campos criados para o formulrio, para que sejam criados os controles Data-Aware. Coloque tambm um DB*a)i/ator apontando para o DataSo%rce. Seu formulrio deve estar semelhante ao mostrado a seguir:
Figura 6. Neste exemplo, vamos processar o clculo de NUM1 e NUM2 e atribuir a ambos os campos calculados, sem cada um de um tipo (um internal e outro no). Para isso, no evento ,nCalcFields do ClientDataSet, digitamos:
procedure &'rm1ain.*lientData!et(*alc'ields+Data!et: &Data!et./ begin inherited/ F calc)lado normalG *lientData!et(*<#*U#<&7D.<sInteger := *lientData!et(NU1(.<sInteger H *lientData!et(NU1A.<sInteger/ Finternal calcG if *lientData!et(.!tate = dsInternal*alc then *lientData!et(IN&7:N<#*<#*.<sInteger := *lientData!et(NU1(.<sInteger H *lientData!et(NU1A.<sInteger/ end/
Execute a aplicao e digite dois valores, para NUM1 e NUM2:
Figura 7. Para ver o campo em ao, coloque dois Bre0points no cdigo conforme mostrado a seguir:
Figura 8. Agora digite um valor qualquer no campo DUMMY, observe que ele no afeta o clculo, pois no serve para nada (no participa da soma). Mesmo assim, note que o cdigo do campo calculado ser executado, mesmo que a soma no seja afetada por DUMMY. O processo no o mesmo para o campo InternalCalc (no segundo brea0point), ele ser executado somente uma vez para fazer a soma, quando voc der o (ost no DataSet (mais otimizado).
Figura 9. Parte < UpdateStatus
Este exemplo mostra como utilizar as propriedades 1pdateStat%s e 1pdateFilter do ClientDataSet. Enquanto o State indica o estado de um DataSet %nte%ro, 1pdateStat%s representa o estado atua# de u+ re-%stro. Stat%sFilter permite exibir filtrar os registros de acordo com seu estado. Inicie uma nova aplicao Delphi VCL e coloque no formulrio principal um ComboBo- e um ClientDataSet. D um clique de direita no ClientDataSet, escolha Load from MyBase $able e abra o arquivo C%stomer.XML. localizado nos demos do Delphi, por padro no diretrio:
*:\<rB)ivos de 0rogramas\<rB)ivos com)ns\Borland !hared\Data
Adicione os $Fields no DataSet e adicione um campo calculado, chamado STATUS, conforme mostrado a seguir:
Figura 1. Arraste os campos para o formulrio para criar os controles Data-Aware. Coloque tambm um DB*a)i/ator apontando para o DataSo%rce. Seu formulrio deve estar semelhante ao mostrado a seguir:
Figura 2. No ,nCreate do form, digite:
uses &y0In5o/ ... procedure &'rm1ain.'orm*reate+!ender: &,b-ect./ var i : integer/ begin for i := ; to $ do *omboBo3(.Items.<dd+=et7n)mName+&y0eIn5o+&U0date!tat)s.@I../ *omboBo3(.Items.<dd+2&odos2./ end/
Isso preenche o Combo com os possveis valores para a enumerao $1pdateStat%s:
&U0date!tat)s = +)sUnmodi5ied@ )s1odi5ied@ )sInserted@ )sDeleted./ &U0date!tat)s!et = set of &U0date!tat)s/
Figura 3. No evento ,nCalcFields digite:
procedure &'rm1ain.*lientData!et(*alc'ields+Data!et: &Data!et./ begin *lientData!et(!&<&U!.<s!tring := =et7n)mName+&y0eIn5o+&U0date!tat)s.@Integer+*lientData!et(.U0date!tat)s../ end/
Isso faz com que o campo STATUS indique o status atual do registro. Quando o usurio selecione um Status na combo, vamos exibir somente os registros que estejam naquele estado (excludo, inserido, modificado etc.). Isso feito no evento ,nChan/e do Combo:
procedure &'rm1ain.*omboBo3(*hange+!ender: &,b-ect./ begin if *omboBo3(.ItemInde3 = D then *lientData!et(.!tat)s'ilter := [] else *lientData!et(.!tat)s'ilter := [&U0date!tat)s+*omboBo3(.ItemInde3.]/ end/
Execute a aplicao, insira, modifique e exclua alguns registros, e a seguir escolha o filtro.
Figura 4. Parte <I Data )s8 De#ta
Este exemplo apresenta o uso das propriedades Data e Delta do ClientDataSet. Data um ,LE3ariant que armazena a cache de dados, Delta um OLEVariant que armazena as ALTERAES feitas em um ClientDataSet. Inicie uma nova aplicao Delphi VCL e coloque no formulrio principal coloque um B%tton, dois ClientDataSets, dois DataSo%rce'. dois DB!ridse um DB*a)i/ator, fazendo as ligaes como mostrado abaixo:
object *lientData!et(: &*lientData!et end
object Data!o)rce(: &Data!o)rce Data!et = *lientData!et( end
object DB=rid(: &DB=rid Data!o)rce = Data!o)rce( end
object *lientData!etA: &*lientData!et end
object Data!o)rceA: &Data!o)rce Data!et = *lientData!etA end
object DB=ridA: &DB=rid Data!o)rce = Data!o)rceA end
object DBNavigator(: &DBNavigator Data!o)rce = Data!o)rce( end
D um clique de direita no ClientDataSet, escolha Load from MyBase $able e abra o arquivo C%stomer.XML. localizado nos demos do Delphi, por padro no diretrio:
*:\<rB)ivos de 0rogramas\<rB)ivos com)ns\Borland !hared\Data
Seu formulrio deve estar semelhante ao mostrado a seguir:
Figura 1. No evento ,nClic0 do boto capturamos o Delta do primeiro CDS e jogamos como Data do segundo CDS:
procedure &'rm1ain.BitBtn(*lic+!ender: &,b-ect./ begin if *lientData!et(.*hange*o)nt I ; then *lientData!etA.Data := *lientData!et(.Delta/ end/
Execute a aplicao. Faa algumas modificaes no primeiro DB!rid e observe que, ao clicar no boto, essas alteraes so registradas no segundo DB!rid:
Figura 2. No exemplo anterior, modifiquei o campo Company de um registro. O CDS mantm os valores originais em Delta para serem utilizados no processo de 4esol)in/ do DataSet(ro)ider, quando os campos precisam ser processados em instrues SQL de atualizao. Parte <II C#%entDataSet e </L
Neste artigo veremos alguns interessantes recursos do ClientDataSet para suporte a XML. Atravs de uma aplicao dbExpress tpica, vamos salvar os dados obtidos em XML para o disco, ler, examinar a estrutura do DataPacket e o XML em memria. O suporte a XML no se restringe simplesmente ao salvamento dos dados para o disco em um formato estruturado. A Borland inclui essa funcionalidade no componente para que possamos facilmente trafegar DataSets pela Web em formato XML, usando o SOAP (Simple ,bect Access (rotocol). Configure uma conexo dbExpress para o banco EMPLOYEE do Interbase. Coloque um SQLConnection apontando para essa conexo. Coloque tambm duas SQLQ%ery, um DataSet(ro)ider, um ClientDataSet, dois DataSo%rces, um DB!rid, quatro B%ttons, um DB*a)i/ator, um Memo e um $5ebBro2ser (paleta Internet). Relacione os componentes conforme mostrado a seguir:
object !"#")ery(: &!"#")ery !"#*onnection = !"#*onnection( End
object Data!etProvider(: &Data!etProvider Data!et = !"#")ery( End
object DB=rid(: &DB=rid Data!o)rce = Data!o)rceA End
object DBNavigator(: &DBNavigator Data!o)rce = Data!o)rceA End
object *lientData!et(: &*lientData!et ProviderName = 2Data!etProvider(2 End
A instruo SQL da SQLQ%ery" mostrada a seguir:
select 8 5rom 71P#,J77
Seu formulrio deve estar semelhante ao mostrado a seguir:
Figura 1.
O cdigo do boto ,pen mostrado a seguir:
procedure &'orm(.btn,0en*lic+!ender: &,b-ect./ begin *lientData!et(.,0en/ end/
O cdigo do boto Sa)e XML mostrado a seguir:
procedure &'orm(.B)ttonA*lic+!ender: &,b-ect./ begin *lientData!et(.!ave&o'ile+2c:\7m0loyee.3ml2./ >ebBrowser(.Navigate+2file:KKKc:\7m0loyee.3ml2./ end/
O cdigo do boto Load XML mostrado a seguir:
procedure &'orm(.B)tton$*lic+!ender: &,b-ect./ begin *lientData!et(.#oad'rom'ile+2c:\7m0loyee.3ml2./ end/
O cdigo do boto XML Data mostrado a seguir:
procedure &'orm(.B)ttonD*lic+!ender: &,b-ect./ begin 1emo(.#ines.&e3t := *lientData!et(.L1#Data/ end/
Execute a aplicao e clique nos botes ,pen. Sa)e XML e XML Data. Observe o resultado na figura a seguir:
Figura 2. A propriedade XMLData, somente-leitura, permite o acesso as dados em XML do ClientDataSet sem a necessidade de salvamento no disco, ideal para por exemplo trafegarmos informaes via Web Services / SOAP. O mtodo Sa)e$oFile salva o Data(ac0et para o disco. Usamos um WebBrowser para exibir o XML dentro do form, atravs de um plugin do Internet Explorer usado pelo componente. Observe que voc pode expandir e reduzir nodes. Tambm possvel fazer isso no browser, claro:
Figura 3. Inclua, exclua, altere alguns registros no DB!rid e salve novamente o XML. Abra o arquivo para examinarmos seu formato.
Um Data(ac0et contm duas sees principais: MetaData e 4o2Data. Na seo MetaData temos informaes que definem os campos do DataSet (Fields) e tambm os parmetros extras que inclumos no Data(ac0et ((arams). Em 4o2Data temos informaes sobre os registros, inclusive que foram modificados. Finalmente, execute a aplicao e carregue o DataSet sem efetuar a consulta ao BD, clicando em Load XML:
Figura 4. Lembre-se que muitas operaes podem ser realizadas em tempo de design, clicando-se de direita sobre o ClientDataSet:
Figura 5. Uma ltima dica que voc pode "roubar dados de qualquer tipo de DataSet em designtime e jogar na memria do ClientDataSet. Por exemplo, coloque uma Table do BDE apontando para DBDemos>Employee.DB. Coloque um ClientDataSet. d um clique de direita sobre ele escolha a opo Assi/n Local Data:
Figura 6.
Figura 7. Observe que os dados foram colocados no Data do CDS (veja o arquivo DFM abaixo):
Figura 8. Agora voc pode acessar os menus de contexto e salvar para XML fcil e rapidamente. Ideal para extrair dados de qualquer banco e salvar rapidamente em XML. Parte <III Ap#%(a7es des(one(tadas Neste exemplo, veremos como usar o dbExpress e ClientDataSet para obter dados do banco de dados, sem no entanto manter uma conexo sempre ativa com o mesmo. Isso far com que suas aplicaes se tornem mais escalveis e apresentem uma melhor performance quando o nmero de clientes aumentar. Para isso, vamos fazer o seguinte: abriremos a conexo e o cursor de dados somente o tempo mnimo necessrio para executar o select. A partir da, o DataSet(ro)ider ir empacotar os dados em um Data(ac0et que ser armazenado no Data do ClientDataSet. A seguir, fechamos a consulta e a conexo, e o usurio poder continuar trabalhando normalmente (com dados em cache). Quando for necessrio aplicar os dados no servidor, abrimos novamente a conexo e enviamos o Delta. O mais interessante de tudo que o prprio DataSet(ro)ider j realiza a maioria desse trabalho para ns: ele sabe exatamente o momento que se faz necessria uma conexo, se encarrega de abri-la, executar a query (que tambm aberta por ele) e assim por diante. Precisamos apenar configurar algumas propriedades. Vamos a prtica. Configure uma conexo dbExpress para o banco EMPLOYEE do Interbase. Coloque um SQLConnection apontando para essa conexo. Coloque tambm um SQLQ%ery, um DataSet(ro)ider, um ClientDataSet, um DataSo%rce, um DB!rid, dois B%ttons e um Memo. Configure os componentes conforme o cdigo a seguir:
object DB=rid(: &DB=rid Data!o)rce = Data!o)rce( end object Data!o)rce(: &Data!o)rce Data!et = *lientData!et( end object *lientData!et(: &*lientData!et ProviderName = 2Data!etProvider(2 end object Data!etProvider(: &Data!etProvider Data!et = !"#")ery( end object !"#")ery(: &!"#")ery !"#*onnection = !"#*onnection( end object !"#*onnection(: &!"#*onnection *onnectionName = 271P#,J772 #oginProm0t = 'alse *onnected = 'alse Mee0*onnection = 'alse Params.!trings = + 2DriverName=Interbase2 2Database=*:\Borland\InterBase\e3am0les\database\em0loyee.gdb2 2User_Name=sysdba2 2Password=masterey2 2!erver*har!et=>IN(ANA2 2!"#Dialect=$2. end
Seu formulrio deve estar semelhante ao mostrado a seguir:
Figura 1.
Figura 2. Observe como configurei as propriedades Connected e 6eepConnection do SQLConnection, ambas para False. A SQLQ%ery tambm tem o Acti)e configurado para False. O cdigo do boto E-ec%tar repassa para a SQLQ%ery a instruo SQL (Select) digitada no Memo:
procedure &'rm1ain.BitBtn(*lic+!ender: &,b-ect./ begin !"#")ery(.!"#.<ssign+1emo(.#ines./ *lientData!et(.*lose/ *lientData!et(.,0en/ end/
Execute a aplicao, digite uma instruo SQL e clique no boto E-ec%tar (veja a figura a seguir).
Figura 3. Quando fazemos a consulta, os seguintes procedimentos foram realizados pelo dbExpress: ClientDataSet (CDS) recebe a chamada ao ,pen; CDS chama o mtodo de interface As7!et4ecords; DataSetProvider (DSP) recebe a chamada ao GetRecords; DSP abre a conexo (SQLConnection); DSP abre o cursor da SQLQuery; DSP varre os dados da consulta; DSP empacota os dados; DSP fecha o cursor (SQLQuery); DSP verifica a propriedade KeepConnection, como est False, fecha a conexo com o BD; DSP envia o DATA ao CDS; CDS fica com dados em memria.
Mesmo com a aplicao em execuo, podemos comprovar que no h nenhuma conexo ativa com o servidor.
Figura 4. O boto Apply simplesmente chama o Apply1pdates para atualizar o BD: o DSP se encarregar de abrir todas conexes necessrias e novamente, fechar aps a operao. Use o 6eepConnection com cuidado: em um cenrio onde o cliente ir frequentemente enviar solicitaes (selects, updates etc.) ao BD, no uma boa soluo ter essa propriedade configurada. Ela ideal para quando o usurio ir trabalhar de forma desconectada, em um conjunto de dados maior, por um longo perodo de tempo. Outro lembrete: no use o (ac0et 4ecords com essa abordagem, pois exige intensa comunicao com o servidor SQL. Pelo motivo anterior, essa combinao no faria sentido. Parte <I, Ca(*e BDE x Ca(*4 C#%entDataSet Neste exemplo vamos estudar um pouco sobre o mecanismo de cache do ClientDataSet. Aproveitarei para fazer alguns comparativos com o mecanismo de cache do BDE, para aqueles que esto migrando de soluo. Ou seja, a aplicao que preparei um comparativo entre o mecanismo de cache do BDE (Cache Updates) e a cache (Data) do ClientDataSet. Configure uma conexo dbExpress para o banco EMPLOYEE do Interbase. Coloque um SQLConnection apontando para essa conexo. Coloque tambm um SQLQ%ery, um DataSet(ro)ider, um ClientDataSet, um DataSo%rce, dois DB!rids e dois B%ttons. Da mesma forma, configure um acesso BDE para o mesmo banco de dados. Os componentes so: DataBase. Q%ery. 1pdateSQL e DataSo%rce. Configure-os como mostrado na listagem a seguir:
Seu formulrio deve estar semelhante ao mostrado a seguir:
Figura 1. Para ver como a cache funciona em ambos os engines, criei um mtodo que adiciona 5 mil registros em um DataSet passado como parmetro. Ele gera valores strings randomizados para cada ccampo da tabela:
procedure &'rm1ain.<00end:andom:ecords+Data!et: &Data!et./ var i : integer/ begin Data!et.,0en/ for i := ( to N;;; do begin Data!et.<00end/ Data!et.'ieldByName+2*U!&_N,2..<sInteger := I/ Data!et.'ieldByName+2*U!&,17:2..<s!tring := :andom!tr/ Data!et.'ieldByName+2*,N&<*&_'I:!&2..<s!tring := :andom!tr/ Data!et.'ieldByName+2*,N&<*&_#<!&2..<s!tring := :andom!tr/ Data!et.'ieldByName+2<DD:7!!_#IN7(2..<s!tring := :andom!tr/ Data!et.'ieldByName+2<DD:7!!_#IN7A2..<s!tring := :andom!tr/ Data!et.'ieldByName+2!&<&7_P:,9IN*72..<s!tring := :andom!tr/ Data!et.P,!&/ <00lication.Process1essages/ end/ end/
No boto Iniciar de cada engine, chamamos o mtodo passando o respectivo DataSet (Query do BDE ou ClientDataSet do DataSnap):
procedure &'rm1ain.BitBtn(*lic+!ender: &,b-ect./ begin <00end:andom:ecords+*lientData!et(./ end/
procedure &'rm1ain.BitBtnA*lic+!ender: &,b-ect./ begin <00end:andom:ecords+")ery(./ end/
Aps iniciar a insero de registros aleatrios, observe que a aplicao consome mais memria (Task Manager) quando se usa o CDS. Isso comprova que todas as alteraes e updates ficam no processo da aplicao, em memria.
Figura 2.
Figura 3. No BDE, so criados arquivos no disco (veja dir. atual da aplicao) para armazenar as atualizaes, como comprova a figura a seguir.
Figura 4.
Figura 5. Com isso, vemos que o DataSnap usa um mecanismo de cache muito mais inteligente e efetivo. Aplicaes com DataSnap sero bem mais escalveis e rpidas. O BDE no foi feito e no est preparado para a criao de solues desse tipo, deficincia que foi suprida pelo dbExpress e ClientDataSet. Parte <, Pa(=et Re(ords Neste exemplo veremos como usar o recurso de (ac0et 4ecords do ClientDataSet. Configurando essa propriedade, podemos instruir ao servidor de aplicao que "empacote e envie dados por demanda. Quando temos um grande select no servidor, podemos instruir ao DataSet(ro)ider que v enviando essas informaes aos poucos, para otimizar o trfego de dados. importante notar desde j, que essa abordagem menos escalvel: necessrio manter o cursor e conexo ativos durante todo o processo. Configure uma conexo dbExpress para o banco EMPLOYEE do Interbase. Coloque um SQLConnection apontando para essa conexo. Coloque tambm um SQLQ%ery, um DataSet(ro)ider, um ClientDataSet, um DataSo%rce, um DB!rid, um DB*a)i/ator, um Memo e um SQLMonitor. Para configurar o recurso em tempo de execuo, coloque tambm um 4adio!ro%p, dois Chec0Bo-es. um SpinEdit. A Figura a seguir mostra como ficou o form:
Figura 1.
A listagem a seguir mostra como cada componente foi configurado:
No boto 4efresh verificamos as configuraes de tela e configuramos o (ac0et 4ecords do ClientDataSet:
procedure &'rm1ain.BitBtnA*lic+!ender: &,b-ect./ begin 1emo(.#ines.*lear/ if *hecBo3(.*heced then *lientData!et(.Pacet:ecords := !0in7dit(.9al)e else *lientData!et(.Pacet:ecords := O(/ *lientData!et(.*lose/ *lientData!et(.,0en/ end/
No ,nChan/e do SpinEdit tambm mudamos essa configurao:
procedure &'rm1ain.!0in7dit(*hange+!ender: &,b-ect./ begin *lientData!et(.Pacet:ecords := !0in7dit(.9al)e/ end/
No boto !et*e-t(ac0et chamamos o mtodo de mesmo nome do ClienDataSet:
procedure &'rm1ain.BitBtn(*lic+!ender: &,b-ect./ begin *lientData!et(.=etNe3tPacet/ end/
No Chec0Bo- com o nome de Fetch,nDemand configuramos a propriedade de mesmo do ClienDataSet:
procedure &'rm1ain.*hecBo3A*lic+!ender: &,b-ect./ begin BitBtn(.7nabled := not *hecBo3A.*heced/ *lientData!et(.'etch,nDemand := *hecBo3A.*heced/ end/
No Chec0Bo- usar (ac0et4ecords fazemos alguns ajustes visuais:
procedure &'rm1ain.*hecBo3(*lic+!ender: &,b-ect./ var i : integer/ begin =ro)0Bo3(.7nabled := *hecBo3(.*heced/ #abel(.7nabled := *hecBo3(.*heced/ !0in7dit(.7nabled := *hecBo3(.*heced/ *hecBo3A.7nabled := *hecBo3(.*heced/ BitBtn(.7nabled := +*hecBo3(.*heced. and not +*hecBo3A.*heced./ end/
No evento ,nLo/$race do SQLMonitor vamos monitorar a comunicao com o IB/FB para verificar quando e quais comandos esto sendo executados, jogando essas informaes para um Memo:
procedure &'rm1ain.!"#1onitor(#og&race+!ender: &,b-ect/ *BIn5o: 0!"#&:<*7Desc./ begin 1emo(.#ines.<dd+!tring+*BIn5o.0s?&race../ end/
Vamos executar a aplicao e fazer alguns testes, usando diferentes configuraes. Dessa forma, vamos entender exatamente como o (ac0et4ecords funciona.
1 - Se+ usar Pa(=etRe(ords - clique no boto 4efresh e veja o comportamento na figura a seguir. Todos os dados retornados pelo Select foram empacotados e jogados na memria (Data) do ClienDataSet. Nenhum recurso do servidor, incluindo conexo e cursor, fica preso. No entanto, o tempo necessrio e trfego de rede usado para transferir o packet maior.
Figura 2. 2 - Usando Pa(=etRe(ords (o+ 5et(*OnDe+and at%)ado - clique no boto 4efresh e veja o comportamento na figura a seguir. Configuramos o packet size como "3, o que indica que os registros sero empacotados de 3 em trs. O prprio ClientDataSet detecta quando mais registros so necessrio e traz por demanda (da o nome Fetch,nDemand). Isso pode acontecer tanto por necessidade do usurio (confirme isso navegando no DBGrid para baixo) ou programaticamente (via chamadas subseqentes ao mtodo *e-t). Observe que cada Fetch envolve, lgico, uma chamada ao servidor SQL. A soluo mais otimizada para grandes resultsets, mais vai consumir mais recursos do BD, pois o cursor e conexo ficam presos aguardando novas solicitaes de packet.
Figura 3. 3 - Usando Pa(=etRe(ords (o+ 5et(*OnDe+and desat%)ado - clique no boto 4efresh e veja o comportamento na figura a seguir. Configuramos o packet size como "3, o que indica que os registros sero empacotados de 3 em trs. A diferena em no usar o Fetch,nDemand que voc deve solicitar novos pacotes de dados, atravs do mtodo !et*e-t(ac0et do CDS (fizemos isso no boto). Faa um teste navegando at o ltimo registro do DBGrid, observe que ele "trava no terceiro. Clicando no boto !et*e-t(ac0et, mais trs registros sero trazidos. Cada Fetch envolve, lgico, uma chamada ao servidor SQL. A soluo mais otimizada para grandes resultsets, mais vai consumir mais recursos do BD, pois o cursor e conexo ficam presos aguardando novas solicitaes de packet.
Figura 4. Parte <,I Sa)e Po%nt Neste artigo veremos como usar o interessante recurso de Sa)e(oint do ClientDataSet. Esse recursos permite que voc "tire um foto do atual status da memria do ClientDataSet, e recupere este status a qualquer momento. Voc pode, por exemplo, salvar as atuais alteraes do CDS em memria, fazer novas alteraes e a seguir desfaze-las, voltando ao estado original previamente sinalizado. Imagine isso como uma espcie de transaes em memria, com 4ollbac0 e Commit. Para ver como isso funciona na prtica, preparei um exemplo interessante. Coloque os componentes no formulrio conforme mostrado na figura a seguir. Aqui colocamos um ClientDataSet. um DataSo%rce e trs B%ttons. D um clique de direita no ClientDataSet, escolha Load from MyBase $able e abra o arquivo C%stomer.XML. localizado nos demos do Delphi, por padro no diretrio C#8Ar9%i)os de pro/ramas8Ar9%i)os com%ns8Borland Shared8Data. Arraste os $Fields para o form para criar os controles Data-Aware.
Figura 1. O c&di$o D'( do )orm * mostrado a se$uir%
object *lientData!et(: &*lientData!et <ctive = &r)e ob-ect *lientData!et(*)stNo: &'loat'ield 'ieldName = 2*)stNo2 end object *lientData!et(*om0any: &!tring'ield 'ieldName = 2*om0any2 !i?e = $; end object *lientData!et(<ddr(: &!tring'ield 'ieldName = 2<ddr(2 !i?e = $; end object *lientData!et(<ddrA: &!tring'ield 'ieldName = 2<ddrA2 !i?e = $; end object *lientData!et(*ity: &!tring'ield 'ieldName = 2*ity2 !i?e = (N end object *lientData!et(!tate: &!tring'ield 'ieldName = 2!tate2 end object *lientData!et(Qi0: &!tring'ield 'ieldName = 2Qi02 !i?e = (; end object *lientData!et(*o)ntry: &!tring'ield 'ieldName = 2*o)ntry2 end object *lientData!et(Phone: &!tring'ield 'ieldName = 2Phone2 !i?e = (N end object *lientData!et('<L: &!tring'ield 'ieldName = 2'<L2 !i?e = (N end object *lientData!et(&a3:ate: &'loat'ield 'ieldName = 2&a3:ate2 end object *lientData!et(*ontact: &!tring'ield 'ieldName = 2*ontact2 end object *lientData!et(#astInvoiceDate: &Date&ime'ield 'ieldName = 2#astInvoiceDate2 end end object Data!o)rce(: &Data!o)rce Data!et = *lientData!et( end object DB=rid(: &DB=rid Data!o)rce = Data!o)rce( end object DBNavigator(: &DBNavigator Data!o)rce = Data!o)rce( end
O cdigo do boto Sa)e(oint salve o estado atual do ClientDataSet:
procedure &'rm1ain.BitBtnA*lic+!ender: &,b-ect./ begin 1yPoint := *lientData!et(.!avePoint/ end/
MyPoint uma varivel declara no form, com o seguinte tipo:
public 1yPoint : integer/
Para recuperar o estado, basta atribuir novamente essa propriedade ao Sa)e(oint, observe (fizemos isso no outro boto):
procedure &'rm1ain.BitBtn$*lic+!ender: &,b-ect./ begin *lientData!et(.!avePoint := 1yPoint/ end/
Para voltar ao ltimo estado, chamamos o UndoLastChanges no boto de mesmo nome:
procedure &'rm1ain.BitBtn(*lic+!ender: &,b-ect./ begin *lientData!et(.Undo#ast*hange+tr)e./ end/
Executando a aplicao, vamos fazer alguns testes para ver como recurso funciona. Altere o Company colocando "TESTE1 ao final, clique em (ost no DB*a)i/ator e a seguir no boto Sa)e(oint. Seguindo os mesmos passos, altere para "TESTE2 e "TESTE3, dando sempre um post e savepoint ao final.
Figura 2. Agora clique vrias vezes no boto 1nLastChan/es e verifique que o estado atual de cada savepoint recuperado a cada chamado a mtodo, como transaes de BD, porm, tudo em memria. Parte <,II Re(ord Count e Re(No Neste artigo veremos como usar as propriedades 4ecordCo%nt e 4ec*o do ClienDataSet. Essas propriedades so exclusivas desse componente, sendo que possuam capacidades limitadas quando se trabalhava com BDE. 4ecordCo%nt, por exemplo, sempre retornava -1 em Queries de consultas a banco de dados quando se usa o BDE. Essa limitao no existe no ClientDataSet, pois o mesmo possui todas as informaes em memria. 4ecordCo%nt - retorne o nmero atual de registros na memria do ClientDataSet, ou seja, o nmero de registros retornados pelo Select associado (a menos que se use (ac0et 4ecords). 4ec*o - a posio atual do cursor local de dados na memria do ClientDataSet. Por exemplo, se esse valor for "5, estamos navegando no quinto registro do ClientDataSet. Vejamos como isso funciona na prtica. Coloque os componentes no formulrio conforme mostrado na figura a seguir. Aqui colocamos um ClientDataSet. um DataSo%rce, um DB!rid. um B%tton e um Label. D um clique de direita no ClientDataSet, escolha Load from MyBase $able e abra o arquivo C%stomer.XML. localizado nos demos do Delphi, por padro no diretrio C#8Ar9%i)os de pro/ramas8Ar9%i)os com%ns8Borland Shared8Data.
Figura 1. No boto simplesmente jogamos o valor de 4ecordCo%nt no Caption do Label.
procedure &'rm1ain.BitBtn(*lic+!ender: &,b-ect./ begin #abel(.*a0tion := Int&o!tr+*lientData!et(.:ecord*o)nt./ end/
Tambm criamos um campo calculado no ClientDataSet, como mostrado a seguir. Ele do tipo Inte/er e vai indicar a posio atual do registro no resulset, se comportando como se fosse um campo normal do BD (como cdigo, ID etc.)
Figura 2.
Figura 3.
No ,nCalcFields atribumos o valor do campo com base no valor do 4ec*o atual do ClientDataSet:
procedure &'rm1ain.*lientData!et(*alc'ields+Data!et: &Data!et./ begin *lientData!et(:7*N,.<sInteger := *lientData!et(.:ecNo/ end/
Observe ambos 4ecordCo%nt e 4ec*o em ao na figura a seguir.
Figura 4. Voc tambm pode fazer um DB!rid "zebrado usado o 4ec*o, bastando verificar se o ndice do registro mpar o par. Isso no possvel no BDE, pois sempre retorna -1. Isso pode ser feito com o seguinte cdigo no evento ,nDra2Col%mnCell do DB!rid:
procedure &'rm1ain.DB=rid(Draw*ol)mn*ell+!ender: &,b-ect/ const :ect: &:ect/ Data*ol: Integer/ *ol)mn: &*ol)mn/ !tate: &=ridDraw!tate./ begin (*DBGrid zebrado*) if not odd+*lientData!et(.:ecNo. then KK se 5or Tm0ar if not +gd!elected in !tate. then KK se a cUl)la n4o estV selecionada begin DB=rid(.*anvas.Br)sh.*olor:= clJellow/ KK de5ine )ma cor de 5)ndo DB=rid(.*anvas.'ill:ect+:ect./ KK 0inta a cUl)la DB=rid(.De5a)ltDrawData*ell+rect@*ol)mn.'ield@!tate./ KK 0inta o te3to 0adr4o end/ end/
O resultado mostrado na figura a seguir:
Figura 5. Parte <I< Usando %nter>a(es Em DataSnap e qualquer arquitetura de objetos distribudos, faz-se uso intensivo de interfaces. importantssimo que voc saiba e compreenda como feita a comunicao entre cliente e servidor de aplicao, atravs do uso desse recurso. Interfaces so usadas amplamente nos bastidores de um arquitetura DataSnap. Dessa forma, decido incluir um captulo introdutrio neste curso, que mostra como usar interfaces "puras (no relacionadas ao DataSnap, mas a POO). Entendendo o princpio bsico aqui proposto, ser base para entendermos abordagens mais complexas quando estudarmos o COM, MTS, COM+ e SOAP. Uma interface semelhante a uma classe que possua somente mtodos abstratos, ou seja, sem implementao. Uma interface apenas define mtodos que depois devem ser implementados por uma classe. Dessa forma, um objeto pode se comunicar com o outro apenas conhecendo a sua interface, que funciona como uma espcie de contrato.
Figura 1. Uma interface como se fosse um controle remoto. Voc consegue interagir com um objeto conhecendo o que ele oferece, tendo a interface que descreve cada funo, porm, sem a mnima idia de como ele implementa essa funcionalidade internamente. Assim como $,bect a classe base para todas as classes do Delphi, a interface base para todas as interfaces IInter>a(e. A interface base para todos as interfaces COM IUn=no"n. I1n0no2n na verdade apenas um alias para IInterface.
Uma Interface no tem cdigo de implementao associado.
Aten!o - O uso de Interfaces um recurso da linguagem Delphi, de forma que voc poder utilizar interfaces mesmo que no esteja programando objetos distribudos.
Veja a seguir a declarao de IInterface:
IInter5ace = interface [2F;;;;;;;;O;;;;O;;;;O*;;;O;;;;;;;;;;DRG2] function ")eryInter5ace+const IID: &=UID/ out ,b-.: P:es)lt/ stdcall/ function _<dd:e5: Integer/ stdcall/ function__:elease: Integer/ stdcall/ end/
Os mtodos 7Add4ef e 74elease definem o mecanismo de conta/em de refer:ncia. Isso significa que voc no precisa liberar um objeto que implementa IInterface. Q%eryInterface faz solicitaes dinamicamente um objeto para obter uma referncia para as interfaces que ele suporta.
Para demonstrar o uso de interfaces vamos criar um pequeno exemplo. Inicie uma nova aplicao no Delphi. Salve o formulrio como "uFrmMain.pas e o projeto "Interfaces.dpr. D o nome de "FrmMain ao formulrio. Abra a unit do formulrio e declara a seguinte interface na seo type.
type I*alc = interface function 1)lti0licar +const 3@y : integer. : integer/ end/
Note que no podemos apertar Shift+Ctrl+C e implementar o mtodo M%ltiplicar. Esse um mtodo de interface, como um mtodo abstract de uma classe. Logo abaixo da interface declare a seguinte classe:
&*om0)tador = class +&Inter5aced,b-ect@I*alc. function 1)lti0licar +const 3@y : integer. : integer/ end/
&*alc)ladora = class +&Inter5aced,b-ect@I*alc. function 1)lti0licar +const 3@y : integer. : integer/ end/
$Comp%tador = (#ass ($Interfaced,bect,ICalc) significa "$Comp%tador herda de $Interfaced,bect e implementa a interface ICalc. Isso n!o herana mltipla.
Observe que ambas as classes $Comp%tador e $Calc%ladora implementam a interface ICalc, e descendem de $Interfaced,bect. $Interfaced,bect se encarrega de implementar a interface IInterface. Aperte Shift+Ctrl+C para declarar os cabealhos dos mtodos.
function &*om0)tador.1)lti0licar+const 3@ y: integer.: integer/ begin res)lt:=38y/ end/
function &*alc)ladora.1)lti0licar+const 3@ y: integer.: integer/ var i : integer/ begin res)lt:=;/ if +3WI;. and +yWI;. then for i:=( to abs+y. do res)lt:=res)ltH3/ end/
Observe que ambas as classes implementam o mtodo M%ltiplicar de ICalc, porm de formas diferentes. Usando Edits. um 4adio!ro%p e um B%tton construa o seguinte formulrio:
Figura 2. D um duplo clique no boto e digite:
procedure &'rm1ain.B)tton(*lic+!ender: &,b-ect./ var ,b- : I*alc/ n(@nA@r : integer/ begin if :adio=ro)0(.ItemInde3=O( then e3it/ case :adio=ro)0(.ItemInde3 of ; : ,b-:=&*alc)ladora.create/ ( : ,b-:=&*om0)tador.create/ end/ n(:=!tr&oInt+7dit(.&e3t./ nA:=!tr&oInt+7ditA.&e3t./ r:=,b-.1)lti0licar+n(@nA./ 7dit$.&e3t:=Int&o!tr+r./ end/
Rode e teste a aplicao.
Figura 3. Parte << Ob?etos CO/ Neste artigo, veremos como criar e como usar objetos COM, um dos fundamentos mais bsicos da programao de objetos distribudos e DataSnap. COM (Component ,bect Model) a tecnologia desenhada pela Microsoft que possibilita a comunicao entre aplicaes clientes e aplicaes servidoras. Essa comunicao feita atravs do que chamamos de interfaces. Uma interface COM a maneira como um objeto expe sua funcionalidade ao meio externo. Um GUID (!lobally 1ni9%e Identifier) um nmero utilizado no COM para identificar uma interface ou uma Co-Class. Quando utilizado para identificar uma interface, um GUID tambm chamado de IID (interface ID). Vamos ento criar nosso primeiro objeto COM. Siga os seguintes passos: Clique em File;*e2| ,ther. Na guia Acti)eX clique em Acti)eX Library. Uma nova biblioteca ser criada, que ser a DLL que conter nosso objeto COM. Salve esta biblioteca com o nome de "LibExemplo. Clique novamente em File;*e2;,ther. Na guia Acti)eX escolha agora C,M ,bect.
Figura 1. Na caixa de dilogo que aparece, digite "Soma para o nome da classe. O Delphi automaticamente preenche o nome da interface a ser implementada (ISoma). Deixe a opo M%ltiple Instance como padro para Instancin/ (isso far com que uma nova instncia de nosso objeto seja criada para cada aplicao cliente). Na opo $hreadin/ Model deixe o padro Apartment (cada objeto COM executado dentro de seu prprio Thread).
Figura 2. Clique em OK. Aparecer a $ype Library do objeto COM. Salve a unidade criada com o nome de uSoma. Uma Type Library constitui a maneira de identificarmos os mtodos suportados por uma interface. O Delphi oferece um editor onde se pode facilmente construir uma interface para um objeto e atravs de cdigo Pascal implementar essa interface. Para visualizar o editor da $ype Library de um objeto voc pode acessar o menu 3ie2;$ype Library. No editor da $ype Library, d um clique de direita sobre a interface ISoma. Escolha *e2; Method. D o nome de "Somar para o mtodo. Em nosso primeiro exemplo, criaremos um funo que receber dois parmetro Sin/le, retornando a soma de ambos. Na opo 4et%rn $ype da guia (arameters, escolha Sin/le. Clique em Add e insira dois parmetros ("Num1 e "Num2) do tipo Sin/le. Salve tudo para o Delphi atualizar a unit de implementao. Tenha em mente que OLE e COM no oferecem suporte a todos os tipos de dados do Delphi.
Figura 3. Clique agora em 3ie2;1nits e escolha LibE-emplo7$LB. Voc ver uma extensa unidade Pascal que define a $ype Library da interface criada. Logo no incio h uma declarao avisando a voc para no mudar este cdigo fonte. Qualquer modificao neste cdigo deve ser feita por meio do editor da $ype Library. Procure pela seguinte declarao:
Esta a definio da nossa interface feita anteriormente no editor da Type Library. O que precisamos fazer agora codificar o mtodo Somar. Abra a unit %Soma e na seo implementation implemente a funo (o Delphi j colocou os cabealhos).
function &!oma.!omar+N)m(@ N)mA: !ingle.: !ingle/ begin res)lt:=N)m(HN)mA/ end/
Agora basta compilar a biblioteca. Clique em (roect;B%ild LibE-emplo. Clique em 4%n;4e/ister Acti)eX Ser)er para registrar o objeto. Vamos agora criar um cliente para nosso objeto COM criado anteriormente. Siga os passos abaixo: Clique em File;*e2 Application. Salve a unit com o nome de "uClienteCOM.pas e o projeto com o nome de "ClienteCOM.dpr. D o nome de "FrmMain e Caption "Objetos COM ao formulrio. Coloque um boto no formulrio, com o Caption "Somar, e trs Edits. Veja a figura:
Figura 4. O objetivo agora clicar no boto e chamar a funo Somar de nosso objeto COM, definido por nossa interface ISoma, implementada em nossa Co-Class $Soma. Para isso precisamos importar a $ype Library do objeto que queremos instanciar. Clique em (roect;Add to (roect e localize o arquivo LibE-emploLib7$LB. Isso faz com que a $ype Library da interface ISoma seja incorporada ao nosso aplicativo. Mas veja bem, apenas a interface do objeto ser conhecida por ns, pois a sua implementao ficar oculta. Ns saberemos que ISoma possui o mtodo Somar mas no sabemos como ela realiza o processamento internamente. Agora no formulrio principal clique em File;1se 1nit e escolha LibE-emplo_TLB. Faa o seguinte no evento ,nClic0 do boto:
procedure &'rm1ain.B)tton(*lic+!ender: &,b-ect./ var ,b- : I!oma/ n(@nA@n$ : single/ begin ,b-:=*o!oma.*reate/ n(:=!tr&oInt+7dit(.&e3t./ nA:=!tr&oInt+7ditA.&e3t./ n$:=,b-.!omar+n(@nA./ 7dit$.te3t:='loat&o!tr+n$./ end/
Execute e veja o resultado como na figura abaixo:
Figura 5. Caso o servidor no tenha sido registrado uma exceo do tipo E,leSysError levantada com a mensagem "Classe no registrada. Parte <<I Ser)%dores de Ap#%(a!o e Re+ote Data /odu#es Utilizamos o DataMod%le para separar o cdigo de acesso a dados da interface de usurio. no DM que utilizamos os $DataSets, como a $SQLQ%ery e a $SQL$able, alm do componente $SQLConnection. Esses componentes so responsveis pela comunicao com o banco de dados (SGBDR). No DM geralmente tambm so implementados procedures que fazem validao sobre alguns dados, introduzem um campo padro ou um campo calculado. Quando compilamos nossa aplicao, todo o cdigo de acesso, clculos e regras ficaro residentes em nosso executvel. Se por algum motivo for necessrio mudar algum parmetro de consulta, alguma instruo SQL, um campo $Field, uma mscara, alguma regra de dados, precisaremos recompilar toda a aplicao. E isso no tudo: os componentes dbExpress usam bibliotecas .dll que devem ser colocadas em todas as mquinas da rede que acessar o banco. Tambm devemos instalar e configurar os conhecidos SQL Clients (clientes de banco SQL), que so bibliotecas especficas para cada SGBDR. Uma soluo separar o acesso em um 4emote DataMod%le.
O 4emote DataMod%le nada mais do que um servidor COM, que basicamente tem a mesma funo de um DM, porm pode fazer uso dos recursos do DCOM. A grande vantagem de se usar um RDM que ele pode ficar totalmente separado do executvel principal da aplicao cliente, residindo em um outro processo, em um outro computador. Surge ento mais uma camada em nosso sistema. Temos a o que se chama de uma aplicao 3 camadas : o cliente, o RDM (constituindo o servidor de aplicao) e o servidor de dados (SGBDR). As aplicaes desenvolvidas utilizando esse tipo de tecnologia tambm so conhecidas como aplicaes M%lti$ier (Multicamadas).
Figura1. Remote DataModule Aqueles procedures que colocamos em nosso antigo DM agora sero implementados dentro de nosso servidor DCOM (o RDM). O cliente conhecendo a interface do nosso objeto DCOM pode ento fazer chamadas a esses procedimentos residentes no RDM, atravs de uma RPC (4emote (roced%re Call) usada pelo DCOM. O mecanismo que possibilita que clientes faam chamadas a funes de objetos residindo em um diferente processo ou em uma diferente mquina se chama marshalin/. O RDM ser responsvel por gerenciar o acesso aos dados e as regras de negcio - chamadas B%siness 4%les (todas as validaes, imposies, clculos, constraints e acessos que envolvem os dados de uma aplicao). Os componentes de acesso do Delphi (BDE, ADO ou IBX) precisam estar somente no servidor de aplicao.
As bibliotecas de clientes SQL tambm no sero necessrias em cada mquina cliente, somente no servidor de aplicao. O cliente s precisa levar consigo a biblioteca dbclient.dll se for feito em Delphi 4 e Midas.dll se feito em Delphi 5 ou superior. Todas as aplicaes clientes acessam a mesma camada (middle< tier), evitando redundncia de regras de negcio, que ficam encapsuladas em uma nica camada compartilhada. As aplicaes clientes ento contm no muito mais do que a interface com o usurio. Para o usurio final uma aplicao MultiTier no muito diferente de uma aplicao tradicional cliente-servidor. Os clientes passam a fazer apenas alguma manipulao de tela e alguma chamadas ao servidor de aplicao (por esse motivo so chamados Thin Clients). Os clientes NUNCA se comunicam diretamente com o servidor de banco de dados. Cr%ando u+ Ser)%dor de Ap#%(a!o DCO/ Comece uma nova aplicao em Delphi clicando em File;*e2|Application. Salve a unit com o nome de "uFrmMain.pas e o projeto como "AppServerDCOM.dpr. D o nome de "FrmMain ao formulrio, caption de "Servidor DCOM Ativado e reduza o seu tamanho. Coloque-o no canto inferior direito da tela. Este formulrio apenas indicar que o servidor DCOM est ativado.
Figura. Formulrio principal do servidor DCOM Agora clique em File|New|Other. No Object Repository, na guia MultiTier, escolha Remote DataModule.
Figura. Criando um Remote DataModule Na caixa de dilogo que aparece, digite "RDM para o nome da Co<Class. Deixe as opes Instancin/ e $hreadin/ Model como esto (estas so as mesmas opes disponveis quando criamos um objeto COM no primeiro exemplo). Salve a unit criada como "uRDM.pas. Coloque um componente SQLConnection e configure o acesso ao banco EMPLOYEE.GDB do Interbase, normalmente situado em "C:\Arquivos de programas\Arquivos Comuns\Borland Shared\data\employee.gdb. Defina seu Lo/in(rompt como False e Connected como $r%e.
Figura. Conectando ao Interbase a partir do Remote DataModule Coloque um SQLDataSet apontando para o SQLConnection e na sua propriedade CommandText di$ite%
select 8 fro/ *U!&,17:
Coloque no RDM o componente $DataSet(ro)ider (paleta DataAccess=. Aponte sua propriedade DataSet para SQLDataSet. Voc deve usar um componente $DataSet(ro)ider para cada $DataSet que utilizar no RDM. Vamos tambm incluir um procedure em nosso servidor, que ser chamado remotamente pelo cliente, que ter a funo de verificar se um determinado CPF vlido (note que a validao - uma regra de negcio - ficar totalmente separada do cliente, e se ela mudar, no precisaremos recompilar ou reinstalar clientes, alm do processamento de clculo da regra ser totalmente feito no servidor de aplicao). Importante: no confunda isso com um Stored (roced%re dos servidores SQL que algo totalmente diferente. Abra o editor da $ype Library clicando em 3ie2;$ype Library. D um clique de direita no editor e escolha *e2;Method. D ao mtodo o nome de 3erificaC(F. Clique na guia (arameters e adicione um parmetro chamado CPF do tipo 5ideStrin/.
Figura. Criando um mtodo no editor da Type Library Abra a unit uRDM e localize a declarao da funo 3erificaC(F. Implemente a funo da seguinte forma:
procedure &:D1.9eri5ica*P'+const *P': >ide!tring./ var d(@dA@n(@nA@na@loo0 : integer/ begin na:=(/ for loo0:=( to #ength+*P'.OA do begin n(:=n(H+((Ona.8!tr&oInt+*P'[loo0]./ nA:=nAH+(AOna.8!tr&oInt+*P'[loo0]./ na:=naH(/ end/ d(:=;/ if +n( /od ((.I=A then d(:=((O+n( /od ((./ dA:=;/ nA:=nAHA8d(/ if +nA /od ((.I=A then dA:=((O+nA /od ((./ if +inttostr+d(.WI*P'[(;]. or +inttostr+dA.WI*P'[((]. then raise 73ce0tion.*reate+2*P' InvVlido2./ end/
Pressione F9 para compilar, registrar e executar o servidor DCOM. Na prxima parte deste curso construiremos a aplicao cliente. Parte <<II C#%entes DataSnap Neste artigo, veremos como construir uma aplicao cliente que far acesso ao servidor de aplicao criado na parte anterior deste curso. Tambm conheceremos mais alguns detalhes envolvidos na construo de aplicaes DataSnap e multicamadas. U+ T*%n C#%ent Clique em File;*e2;Application. Salve a unit com o nome de "uFrmMain.pas e o projeto com o nome de "DataSnapClient.dpr. D ao formulrio o nome de FrmMain e caption de "DataSnap Client. Crie um DataMod%le clicando em File;*e2;DataMod%le. Salve sua unit como "uDM.pas e d a ele o nome de "DM. Coloque no DM um componente $DC,MConnection da guia DataSnap. Na propriedade Ser)er*ame escolha nosso servidor de aplicao registrado, chamado neste caso de AppSer)erDC,M.4DM.
Figura. Configurando ServerName do DCOMConnection Configure a propriedade Connected para $r%e. A janela principal do servidor de aplicao deve aparecer neste momento. Coloque um componente ClientDataSet. Configure sua propriedade 4emoteSer)er para o DC,MConnection. Na propriedade (ro)ider*ame escolha o DataSet(ro)ider (que est no RDM, que por sua vez aponta para o SQLDataSet de C%stomers). Configure a propriedade Acti)e para $r%e. Os dados da tabela CUSTOMERS sero trazidos do servidor de aplicao. D um duplo clique no ClientDataSet e adicione todos os campos $Fields. Seu DM deve estar como mostrado a seguir.
Figura. DataModule da aplica!o cliente Pro)%d%n- Quando o ClientDataSet recebe os dados vindos do servidor de aplicao, os mesmos so decodificados, pois vieram empacotados em formato ,LE3ariant. No servidor de aplicao, o DataSet(ro)ider se encarrega de distribuir os dados empacotados aos clientes, numa operao chamada pro)%d%n-. Voc pode incluir dados personalizados neste pacote se quiser, como hora do sistema, usando o evento ,n!etDataSet(roperties do DataSet(ro)ider. 5or+u#@r%o pr%n(%pa# Volte ao formulrio principal. Aperte Alt+F11 e na lista escolha uDM. Coloque um DataSo%rce da guia DataAccess e na propriedade DataSet aponte para o ClientDataSet que est no DM. Usando DBEdits e Labels (alm de um DB*a)i/ator) construa o formulrio mostrado a seguir:
Figura. "#in$Client DataSnap Coloque um B%tton, d o caption de "Apply e no evento ,nClic0 digite o seguinte :
D1.*lientData!et.<00lyU00dates+O(./ Reso#)%n- e Re(on(%#e Apply1pdates aplica a cache do ClientDataSet (chamada DELTA) no servidor de aplicao. O servidor de aplicao ir tentar aplicar a cache recebida no servidor de banco de dados por meio do componente DataSet(ro)ider (reso#)%n-). Se algum erro for constatado, o servidor de aplicao devolve os dados ao cliente, para que sejam manipulados no evento ,n4econcileError do ClientDataSet. C*a+ando o +'todo de ne-:(%o Coloque um boto no formulrio (com o caption "CPF) e no seu evento ,nClic0 digite:
procedure &'rm1ain.B)ttonA*lic+!ender: &,b-ect./ var *P' : string/ begin *P':=In0)tBo3+2*hamando mUtodo remoto2@2")al o nX do se) *P'Y2@22./ D1.D*,1*onnection.<00!erver.9eri5ica*P'+*P'./ !how1essage+2*P' vVlido2./ end/
Pressione F9 para executar a aplicao cliente. Insira alguns registros. Teste a validao do CPF. No esquea de pressionar Apply depois de gravar um registro, caso contrrio, a cache (delta) no ir para o servidor de aplicao e seus dados no sero realmente gravados no servidor de banco de dados. A chamada ao mtodo Apply1pdates do ClientDataSet dispara o evento ,n1pdateData e Before1pdate4ecord do DataSet(ro)ider no servidor de aplicao, onde voc pode trocar alguns valores, decodificar dados, inserir novos valores antes dos dados serem realmente aplicados no banco. Sa>eCa## Observe que demos um ra%se no servidor de aplicao quando um CPF for invlido. Essa exceo entretanto no levantada no servidor de aplicao, o que poderia ser fatal. Observe a declarao do mtodo 3erificaC(F:
Sa>eCa## a diretiva que protege uma mtodo contra excees. Qualquer exceo que for levantada dentro da pilha da chamada do mtodo ser propagada para a aplicao cliente. A %nter>a(e IAppSer)er Neste cliente usamos a vinculao dinmica com o servidor de aplicao, atravs do uso da propriedade AppSer)er. Como AppServer do tipo 3ariant, o Delphi no pode verificar na compilao se o mtodo 3erificaC(F realmente existe no servidor de aplicao. Isto feito em tempo de execuo (vnculo dinmico). Experimente digitar "VerCPF e nada acontece at o mtodo ser finalmente chamado. Se um mtodo no encontrado no servidor uma exceo do tipo E,leError gerada:
Figura. %&nculo din'mico ( c#amadas s!o resolvidas em tempo de e)ecu!o Poderamos ter usado vnculo esttico (importando a $ype Library) da mesma forma como fizemos no primeiro exemplo, para que o compilador, j em design-time, identifica-se os mtodos suportados pela nossa interface I4DM (observe tambm que nossa interface I4DM derivada da interface IAppSer)er). AppSer)er propriedade de $DispatchAppSer)er, que uma das classes base de $DC,MConnection. $DispatchAppSer)er implementa os mtodos definidos por IAppSer)er.
Parte <<III Introdu!o ao CO/A Como vamos, um dos problemas do DCOM a sua escalabilidade. Isto , ele no apresenta os mesmo resultados quando h um aumento do nmero de conexes, perdendo desempenho. Objetos DCOM mantm informaes persistentes sobre cada cliente que instancia um objeto do seu tipo, e s ir liberar tais recursos quando a varivel sair do escopo no programa cliente. Devido aos objetos DCOM permanecerem ativos com informaes sobre o cliente e sua ativao, so conhecidos como objetos com estado. O sucessor do DCOM foi o MTS (Microsoft $ransaction Ser)er). Ele incorporou diversos recursos a essa tecnologia. O MTS instalado como parte do Windows NT 4 Server (,ption (ac0). Diferente do DCOM que no possui um gerenciador prprio (a no ser o pequeno DCOMCNFG.EXE), o MTS utiliza o MMC (Microsoft Mana/ement Console) para configurar e gerenciar objetos. O COM + o sucessor do MTS (mais o MSMQ - Microsoft Messa/e Q%e%e), e herda deles inmeros recursos: gerenciamento centralizado, pooling de objetos, suporte a transao e escalabilidade. O COM+ acompanha o Windows 2000/XP, formando a chamada camada lgica de negcio em um sistema Distribudo (Multicamadas).
Figura. COM * atuando na camada l+gica de neg+cio em uma ar,uitetura distribu&da Cr%ando (o+ponentes CO/A Clique em File|New|Active X|Tranactional O!"ect.
Figura. Criando um ob-eto COM* Na caixa de dilogo que aparece digite "Soma para CoClas *ame.
Figura. Op.es de cria!o de um ob-eto COM* Pressione File|Sa)e All e salve a unit como "uSoma.pas e o projeto como "LibSoma.dpr. Da mesma forma que fizemos para o objeto COM criado neste mesmo mdulo, crie um mtodo chamado Somar que aceite dois parmetros do tipo Sin/le.
Figura. /ditor da "0pe 1ibrar0 Implemente a funo na unit da seguinte forma:
function &!oma.!omar+3@ y: !ingle.: !ingle/ begin res)lt:=3Hy/ end/
Clique em Project|Build LibSoma. T/tsAutoOb?e(t A classe $MtsA%to,bect encapsula objetos e interfaces de um servidor de aplicao MTS.
&1ts<)to,b-ect = class+&<)to,b-ect@ I,b-ect*ontrol. private ',b-ect*onte3t: I,b-ect*onte3t/ '*anBePooled: Boolean/ protected F I,b-ect*ontrol G procedure <ctivate/ safecall/ procedure Deactivate/ stdcall/ function *anBePooled: Bool/ virtual/ stdcall/
procedure ,n<ctivate/ virtual/ procedure ,nDeactivate/ virtual/ property ,b-ect*onte3t: I,b-ect*onte3t read ',b-ect*onte3t/ public procedure !et*om0lete/ procedure !et<bort/ procedure 7nable*ommit/ procedure Disable*ommit/ function IsIn&ransaction: Bool/ function Is!ec)rity7nabled: Bool/ function Is*allerIn:ole+const :ole: >ide!tring.: Bool/ property Pooled: Boolean read '*anBePooled write '*anBePooled/ end/ Insta#ando o ser)%dor CO/A Voc pode instalar o objeto COM+ de duas formas: Usando o gerenciador MMC chamado Ser)i>os de Componente Usando a opo Install C,M? ,bects disponvel na IDE do Delphi. Usaremos a segunda opo. Clique em 4%n;Install C,M? ,bects.
Figura. Instalando um servidor COM* Preencha as opes como mostra a figura a seguir.
Figura. Instalando um servidor COM* Aten!o& Quando voc instala a aplicao no catlogo do COM+ ela estar automaticamente registrada. Cr%ando o (#%ente Clique em File;*e2;Application. Clique em File;Sa)e All e salve a unit como "uCliSoma.pas e a aplicao como "CliSoma.dpr. Clique em (roect;Add to (roect e adicione o arquivo LibSoma7$LB.pas, que a $ype Library do servidor criado anteriormente. No formulrio principal aperte ALT+F11 e adicione a TLB clusula uses. No evento ,nClic0 do boto digite:
Figura. Cliente para um servidor COM* Parte <<I, CO/A e DataSnap Nesta parte do curso, veremos como utilizar DataSnap integrado ao COM+, a soluo mais indicada para a construo de aplicaes de BD multicamadas. DataSnap no /TSBCO/A No Delphi 7, clique em File|*e2|,ther. No ,betct 4epository, na guia M%lti$ier, clique sobre $ransactional Data Mod%le.
Figura. Cliente para um servidor COM* Digite "RDM para o nome da Co-Class e deixe as demais opes como padro. Salve o projeto como "AppServerCOMPlus.dpr. Configure o DataMod%le usando os mesmos componentes do exemplo anterior, como mostrado a seguir:
Figura. Conectando ao Interbase a partir do Remote DataModule Clique em (roect;B%ild AppSer)erCom(l%s. Ser)%os de Co+ponentes - Insta#ando o ob?eto CO/A Ao invs de utilizaremos a IDE do Delphi para instalar o objeto agora vamos usar o Ser)i>os de Componente. Clique em Iniciar;(ainel de Controle;Ferramentas Administrati)as;Ser)i>os de Componente. Expanda o item Ser)i>os de componente e clique de direita sobre Aplicati)os C,M?.
Figura. Criando um novo aplicativo no COM* Na janela que aparece clique em *e-t. Depois clique em Criar %m aplicati)o )a@io.
Figura. Criando um novo aplicativo no COM* D o nome de "AppServerCOMPlus ao aplicativo. Depois clique em A)an>ar e Concl%ir. Expanda o novo aplicativo criado e d um clique de direita em Componentss. e escolha *o)o Componente. Clique em A)an>ar e depois escolha Instalar no)oAs= componenteAs=.
Figura. Instalando um componente no aplicativo no COM* Localize a DLL da aplicao servidora criada anteriormente no Delphi (AppSer)erCom(l%s.dll). Clique em A)an>ar e Concl%ir. Veja na figura a seguir nosso componente instalado no catlogo do COM+.
Figura. Componente instalado no COM* T*%n-C#%ent para o ser)%dor CO/A Agora usaremos o mesmo cliente construdo para o servidor DCOM construdo anteriormente, chamado DataSnap Client. Aps abrir o projeto v at o DM e adicione um segundo DC,MConnection. dando a ele o nome de "MTSConnection. Altere sua propriedade Ser)er*ame para apontar para o novo servidor COM+. Defina o 4emoteSer)er do ClientDataSet como M$SConnection. Reconecte o ClientDataSet.
Figura. Configurando o ServerName do DCOMConnection Des#%-ando e In%(%ando pa(otes Para retirar a DLL da memria do servidor e parar um pacote, basta que voc d um clique de direita na aplicao e escolha Desli/ar. Para reiniciar escolha a opo 4einiciar. Lembre-se que o COM+ B%st<In<$ime Acti)ation, logo o pacote ser carregado assim que um cliente instanciar um objeto. Se voc escolher a opo Desati)ar, ento o pacote no ser carregado automaticamente a menos que algum o inicie.
Figura. Desligando e reiniciando um pacote COM* Parte <<, SOAP e CebSer)%(es Ap#%(a7es Ser)%doras At agora vimos que a tecnologia COM permite que objetos sejam distribudos em uma rede local. Porm o COM possui algumas limitaes: um padro proprietrio; No multi-plataforma; Apresenta problemas em rede com firewalls; SOAP Simple ,bect Access (rotocol - mecanismo que permite a troca de mensagens entre servidores e clientes. Padro aberto; Roda no topo do protocolo HTTP; Dados trafegam em XML; Multi-plataforma; Ceb Ser)%(es 2BDC )s BDB3 Quando voc precisa consultar a cotao on-line do dlar, voc geralmente abre um browser e acessa um determinado site que fornea a cotao em tempo real. Se voc precisa consultar o CEP de uma pessoa, precisa abrir um browser e entrar no site dos correios. Se precisar consultar a disponibilidade de vos de uma empresa rea, precisa entrar em seu site. Ou seja, tudo feito por um browser (cliente) acessando um servio (business). BDC
Browser HTTP/ HTML Servidor Figura. 2plica!o 3usiness to Consumer 435C6 Uma aplicao B2B (Business to Business), ao invs de distribuir dados em formato HTML, distribui dados em formato XML. Isso permite que outras aplicaes (que no somente um browser) possam se comunica com esse servio e retirar dele informaes. Essas informaes podem ser colocadas em um formulrio do Delphi, do Visual Basic, do Kylix, em uma pgina PHP, em uma planilha do Excel, etc. Isso , voc desenvolvedor (um lado do negcio) utiliza servios de outra empresa (o outro lado) para disponibilizar informaes para o seu cliente final. Ex.: Uma agncia de turismo desenvolve uma HomePage para que os usurios possam agendar viagens pelo mundo inteiro. Porm, a empresa gostaria de oferecer um pacote completo, incluindo dirias de hotel, pesquisa de preos em companhias areas, disponibilizada de transporte na cidade, alimentao, etc. Para isso, um atendente teria que entrar no site de cada um das empresas, anotar horrios, conferir com horrios e disponibilidades de vagas nos hotis, etc. Se essas empresas disponibilizarem Web Services, a agncia de turismo pode automatizar esse processo enviando requisies SOAP para cada Web Service e montando o pacote final, com base nas informaes fornecidas pelo cliente. Uma aplicao Web Service Consumer no precisa necessariamente ser um browser. Uma aplicao desse tipo pode ser escrita em praticamente qualquer linguagem, utilizando interface dedicada ao invs de um browser. CSDL 5eb Ser)ices Description Lan/%a/e - a WSDL descreve os mtodos de um 5eb Ser)ice. Clientes que queiram utilizar os servios de um 5eb Ser)ice devem conhecer sua interface, atravs da WSDL. A WSDL semelhante a uma $ype Library, a diferena que est em formato XML. Cr%ando u+ Ceb Ser)%(e Clique em File;*e2;,therC5ebSer)ices>S,A( Ser)er Application.
Figura. Criando um 7ebService Na caixa que aparece escolha a opo 5eb App Deb%//er e-ec%table. Em Class *ame digite "Soma.
Figura. Definindo o tipo de servidor O Delphi perguntar se voc quer criar uma interface para o mdulo SOAP. Responda Des.
Figura. Criando uma interface Preencha as opes como mostra a figura a seguir.
Figura. Op.es da interface do servio Aperte OK. Clique em File;Sa)e All. Salve a %nit".pas como "uFrmMain.pas, a %nit'.pas como "uWM.pas, deixe o nome sugerido para SomaIntf.pas e SomaImpl.pas e salve o projeto como "SomaService.dpr. Selecione o formulrio e d a ele o nome de "FrmMain. Selecione o 5ebMod%le e d a ele o nome de "WM.
Figura. 7ebModule da aplica!o
Abra a unit SomaIntf. Declare o mtodo Somar como mostrado a seguir.
Como voc pode ver, nossa interface ISoma descende da interface IIn)o0able. Repare tambm que nessa unit apenas definimos a interface. A implementao feita em uma unit parte. IIn)o=ab#e - de>%n%ndo Assim como I1n0no2n a interface base para todas as interfaces COM, IIn)o0able a interface base para todas as interfaces que definem 5eb Ser)ices. Veja sua declarao:
Como voc pode ver, sua declarao bastante simples. Na verdade, essa interface apenas herda de IInterface e ativa o recurso de RTTI (diretiva $M). Nesse caso RTTI se faz necessrio para que as chamadas de mtodos contidas em um pacote SOAP possam ser decodificadas e repassadas ao mtodo apropriado da interface IIn)o0able. Ou seja, o servidor deve ser capaz de chamar um mtodo e passar parmetros apenas lendo os dados em string contidos no pacote SOAP. TIn)o=ab#eC#ass - %+p#e+entando $In)o0ableClass a classe base para todas as classes que implementam interfaces IIn)o0able. Esta classe semelhante a classe $Interfaced,bect usada em aplicaes COM. Abra a unit SomaImpl e implemente o Web Service dessa forma:
unit !omaIm0l/
interface
uses Invoe:egistry@ &y0es@ L!B)iltIns@ !omaInt5/
type
&!oma = class+&Invoable*lass@ I!oma. public function !omar+N)m(: !ingle/ N)mA: !ingle.: single/ stdcall/ end/
i/ple/entation
F &!oma G
function &!oma.!omar+N)m(@ N)mA: !ingle.: single/ begin res)lt:=N)m(HN)mA/ end/
Execute a aplicao. Na prxima parte, veremos como criar uma aplicao para consumir este WebService. Parte <<,I SOAP e CebSer)%(es Ap#%(a7es (#%entes Nesta parte do curso, veremos como criar uma aplicao que far acesso ao Web Service criado na parte anterior. At%)ando o Ser)%dor Normalmente voc vai hospedar seu Web Service em um servidor IIS ou Apache. Em nosso caso, estamos usando um servidor especial de depurao, feito pela Borland, o Web App Debugger. Esse utilitrio dispensa o uso de um servidor Web comercial, porm deve ser usado somente para testes e depurao. Para ativ-lo clique no menu $ools;5eb App Deb%//er. No console do servidor clique no boto Start.
Figura. 7eb 2pp Debugger Clique no link que aparece no console. No navegador escolha nossa aplicao na lista e aperte !o.
Figura. Descri!o do servio Clique no link 5SDL ao lado de ISoma.
Figura. 7SD1 da interface Copie a URL que aparece na barra de endereos do navegador. Deixe o 5eb App Deb%//er rodando. Agora vamos fazer a aplicao cliente. C#%ente Ceb Ser)%(e B Ceb Ser)%(e Consu+er Clique em File|New A##lication$ Sal#e a unit com o nome de +uClienteSO"P.pas, e o pro-eto com o nome de +ClienteSO"P.dpr,. D. o nome de +'rm(ain, ao )ormul/rio e Ca#tion +0e1 Ser#ices 2 SO"P,. Coloque um 1oto no )ormul/rio3 com o Ca#tion +Somar,3 e tr.s %dit. 4e-a a )i$ura%
Figura. 2plica!o cliente para servidor SO28 O o1-eti#o a$ora * clicar no 1oto e c5amar a )uno Somar do 0e1 Ser#ice3 tra)e$ando os dados em 6(L78TTP at* o ser#idor. I+portando a CSDL Clique em File;*e2;,ther;5eb Ser)ices;5SDL Importer.
Figura. Importando a 7SD1 do servidor SO28 No 5SDL Import 5i@ard cole a URL que voc copiou anteriormente.
Figura. 7SD1 Import 7i9ard Clique em *e-t e depois Finish. Salve a unit criada. Observe que o Delphi cria uma interface IIn)o0able na aplicao cliente baseado nas informaes da 5SDL. Essa interface exatamente a mesma que foi definida quando criamos o servidor SOAP.
Figura. Interface gerada a partir do documento 7SD1 Volte ao formulrio principal. Aperte Alt+F11 e escolha a unit ISoma" na lista. Coloque no formulrio um componente HTTPRIO (primeiro componente da paleta 5ebSer)ices).
Figura. :""8RIO $ Remote Invo;able Ob-ect Defina sua propriedade 5SDLLocation, Ser)ice e (ort.
Figura. Configurando o :""8RIO No evento ,nClic0 do boto digite:
0roced)re &'rm1ain.B)tton(*lic+!ender: &,b-ect./ var ,b- : I!oma/ n(@nA@n$ : single/ begin ,b-:=+P&&P:I,( as I!oma./ KK ,b-:=*o!oma.*reate/ n(:=!tr&oInt+7dit(.&e3t./ nA:=!tr&oInt+7ditA.&e3t./ n$:=,b-.!omar+n(@nA./ 7dit$.te3t:='loat&o!tr+n$./ end/
Observe que a nica diferena do cdigo do cliente SOAP para o cliente COM o modo como o objeto criado.
Figura. Soma feita no servidor SO28 Quando o usurio aperta o boto Somar, os nmeros "3 e "4 so codificados em um pacote SOAP/XML e enviados ao Web Service. O Web Service recebe o nome do mtodo a ser chamado (Somar) e usando RTTI invoca o mtodo de interface apropriado, por meio dos componentes E$$(SoapDispatcher e E$$(Soap(ascalIn)o0er.
Parte <<,II DataSnap e SOAP Continuando o nosso curso, veremos neste artigo como criar uma soluo simples multicamadas usando DataSnap e SOAP. Clique em File;*e2;,therC5ebSer)ices>S,A( Ser)er Application. Na caixa de dilogo que aparece d o nome de "AppServerSOAP para o servidor 5eb App Deb%//er.
Figura. Criando um servidor SO28 < DataSnap Responda *o quando o Delphi perguntar se voc quer criar uma interface. Salve a %nit".pas como "uFrmMain.pas, a %nit'.pas como "uWM.pas e o projeto como "AppServerSOAP.dpr. Selecione o formulrio e d a ele o nome de "FrmMain. Selecione o 5ebMod%le e d a ele o nome de "WM. Clique em File;*e2;,therC5ebSer)ices>S,A( Ser)er Data Mod%le. Na janela que aparece digite "RDM para a opo Mod%le *ame.
Figura. Criando um SO28DataModule Salve a unit criada como "uRDM. Configure o DataMod%le usando os mesmos componentes do exemplo anterior.
Figura. SO28DataModule Observe que a interface I4DM descende de IAppSer)erS,A(. A classe $4DM descende de $SoapDataMod%le e implementa I4DM. IAppSer)erS,A( e IAppSer)er.
Execute a aplicao. Ati)e o 5eb App Deb%//er. Faremos agora a parte cliente da aplicao. SOAP Conne(t%on Abra a aplicao DataSnap Client criada anteriormente. Coloque no DM um componente SoapConnection da paleta 5ebSer)ices.
Figura. =sando SO28Connection Configure sua propriedade 14L para:
Configure Connected para $r%e. Conne(t%onBro=er Coloque no DM um componente ConnectionBro0er da paleta DataSnap.
Figura. Connection3ro;er abstrai a cone)!o DataSnap Esse componente abstrai para o ClientDataSet o tipo de conexo que ser utilizada. Por exemplo, voc pode trocar sua conexo de DCOM para SOAP ou ainda COM+ sem precisar alterar os ClientDataSets. Figura. DataModule cliente Aponte a propriedade Connection desse componente para S,A(Connection. Aponte a propriedade 4emoteSer)er do ClientDataSet para ConnectionBro0er". Testando o (#%ente DataSnap B SOAP Execute a aplicao.
Figura. "estando a aplica!o DataSnap client com o servidor SO28 Parte <<,III /u#t%(a+adas no De#p*% DEEFBDEEG (o+ 8NET Re+ot%n- Muitas tecnologias existem atualmente, destinadas a proverem a comunicao entre aplicaes. Essas aplicaes podem ser dois processos rodando na mesma mquina, ou em duas mquinas de uma rede. Exemplos de tecnologias distribudas so: COM/DCOM/COM+, RMI, CORBA, SOAP, RPC etc. No .NET, a tecnologia responsvel pela construo de aplicaes distribudas se chama .NET Remoting. Essa tecnologia substitui o antigo DCOM/COM+ (apesar de ainda poderem ser usados no .NET) e oferece mecanismos que permite a comunicao entre clientes e servidores. Observe que a arquitetura de objetos distribudos semelhante arquitetura de aplicaes de BD cliente / servidor. A diferena crucial que o cliente estar usando obetos armazenados no servidor, e no tabelas como em um sistema cliente/server. Ao invs de passarmos instrues SQL, fazemos chamadas para mFtodos remotos. Ar.u%tetura do 8NET Re+ot%n- A 5%-ura H mostra a arquitetura do .NET Remoting. Temos uma aplicao servidora e uma aplicao cliente, que se comunicam atravs de um Channel (discutido a seguir). O objeto remoto independe de localizao.
Figura. .N/" Remoting> C#annel? Formatters? 8ro)ies e modos de ativa!o Ob?etos Re+otos& /ars*a#-bI-Re>eren(e e /ars*a#-bI-,a#ue No .NET, existem dois tipos de objetos usados em aplicaes distribudas: Marshal<By<4eference - so passados por referncia, so criados remotamente no servidor e so ento controlados remotamente pela aplicao cliente. A aplicao cliente faz uso de um pro-y, que se encarrega de repassar as chamadas para o servidor remoto; Marshal<by<3al%e - so objetos passados por valor e precisam ser serializados para o transporte via rede. devem implementar a interface ISeriali@able ou receber o atributo Seriali@able. U+ exe+p#o pr@t%(o Neste captulo criaremos um exemplo bastante prtico e original: um Chat que usar o conceito de objetos distribudos e .NET Remoting para trocar mensagens entre usurios da rede. Nossa aplicao ser composta de trs partes: Ser)er < aplicao servidora que conter o objeto remoto que controla o chat; Client G aplicao Windows Forms que permitir enviar e receber mensagens; Intf G pacote que define classes e interfaces destinadas a comunicao entre cliente e servidor (o "contrato). Esse "contrato usado por ambas as aplicaes, portanto vamos comear fazendo a definio da interface. Inter>a(es& (ontrato entre ser)%dor e (#%ente Um dos fundamentos de qualquer sistema de comunicao de objetos distribudos o uso de uma interface. Uma interface semelhante a uma classe, mas que possui somente mtodos abstratos, ou seja, sem implementao. Esses mtodos devem ser implementados por uma classe. Dessa forma, um objeto pode se comunicar com o outro apenas conhecendo a sua interface, que funciona como uma espcie de contrato (5%-ura).
Figura. =ma interface um @contratoA entre cliente e servidor Uma interface como se fosse um controle remoto. Voc consegue interagir com um objeto remoto conhecendo o que ele oferece, tendo a interface que descreve cada funo, porm, sem a mnima idia de como ele implementa essa funcionalidade internamente. Cr%ando a %nter>a(e No Delphi 2005, clique em FileC*e2C,therCDelphi for .*E$ (roectsC(ac0a/e. Salve o com o nome de (ac0Intf.dp0 em um diretrio chamado Intf. A seguir, adicione uma unit ao pacote (FileC*e2C,therC Delphi for .*E$ (roectsC*e2 FilesC1nit) e salve-a com o nome de ChatIntf. Altere o cdigo da unit como mostrado a seguir:
unit *hatInt5/
interface
type [!eriali?able] *hat1sg = class Nome: !ystem.!tring/ 1sg: !ystem.!tring/ DataPora: !ystem.Date&ime/ end/ *hat1sgs = array of *hat1sg/
I*hat = interface procedure 7nvia1ensagem+1sg: *hat1sg./ function :e5resh: *hat1sgs/ end/
i/ple/entation
end.
Observe que definimos uma classe chamada ChatMs/, que representa uma mensagem do chat. Ela possui os atributos *ome. Ms/ e DataEora. que representam o nome do usurio que postou a mensagem (*ome), a mensagem em si (ms/) e a data/hora em que foi postada. Como essa mensagem no ser controlada remotamente (ela deve ser completamente serializada e enviada pela rede), a marcamos com o atributo Seriali@able. Tambm definimos um array de mensagens, chamado ChatMs/s. A seguir, criamos uma interface chamada IChat. Essa interface define o "protoloco de comunicao entre cliente e servidor, ou seja, uma espcide de "contrato. O cliente deve conhecer seus mtodos para poder interagir com o servidor. O mtodo En)iaMensa/em recebe um Ms/, que a mensagem propriamente dita que ir trafegar pela rede. 4efresh retorna todas as mensagens enviadas ao chat (um array de ChatMs/). Pronto, basta compilaro Assembly clicando em (roectCB%ild. Isso ir gerar uma DLL. Cr%ando o ser)%dor re+oto Nosso servidor consistir de uma aplicao Windows Forms, que ter o formulrio somente para fins de ativao e para manter a aplicao em execuo. Inicie ento uma nova aplicao Windows Forms, dando o nome de Ser)er.dpr ao projeto.
D%(a: use um grupo ((roect !ro%p) para gerenciar facilmente os trs projetos desta aplicao. Para isso, clique de direita sobre o (roect!ro%p" que est no (roect Mana/er e crie ou adicione os projetos que desejar.
Figura. 8ro-ect group facilita o acesso ao pacote? cliente e servidor Conforme comentei anteriormente, a interface usada por ambos os projetos, cliente e servidor. O servidor implementa o chat, enquanto o cliente utiliza seus servios. Ento, precisamos adicionar uma referncia no projeto servidor para o Assembly criado anteriormente. Para isso, clique de direita no nome do projeto no (roect Mana/er e escolha Add 4eference. Na janela que aparece, vemos na lista superior apenas os Assemblies registrados no GAC. Como nosso assembly privado, clique no boto Bro2se e localiza o (ac0Intf.dll, selecione-a na lista e clique em OK. Adicione uma nova unit ao projeto (FileC*e2C,therC Delphi for .*E$ (roectsC*e2 FilesC1nit), salve-a com o nome de %ChatSer)er.pas e digite o seguinte:
unit )*hat!erver/
interface
uses *hatInt5/
type *hat!erver = class +1arshalBy:e5,b-ect@I*hat. 1sgs: *hat1sgs/ 0roced)re 7nvia1ensagem+1sg: *hat1sg./ 5)nction :e5resh: *hat1sgs/ end/
i/ple/entation
function *hat!erver.:e5resh: *hat1sgs/ begin res)lt := 1sgs/ end/
procedure *hat!erver.7nvia1ensagem+1sg: *hat1sg./ var i: integer/ begin i := #ength+1sgs. H (/ msg.DataPora := !ystem.Date&ime.Now/ !et#ength+1sgs@i./ 1sgs[i O (] := 1sg/ end/
A clusula %ses faz referncia unit ChatIntf.pas, contida no assembly (ac0Intf.dll. A seguir, temos a declarao da classe ChatSer)er:
*hat!erver = class +1arshalBy:e5,b-ect@I*hat.
Aqui estamos dizendo que a classe descende de MarshalBy4ef,bect, indicando que se trata de um objeto que ser controlado remotamente e que implementa os mtodos da interface IChat. O mtodo En)iaMensa/em simplesmente guarda a mensagem em um array (Ms/s) que permanece ativo durante toda a vida til do objeto remoto. 4eresh simplesmente devolve ao cliente todas as mensagens postadas. Volte ao formulrio principal e no seu evento Load digite o seguinte:
Vamos conhecer um pouco mais sobre o que significam os objetos e mtodos usados no cdigo anterior. C*anne#s e 5or+atters O namespace System.4%ntime.4emotin/.Channels contm classes para suporte a channels (canais), que so usados como meio de transporte para chamadas de mtodos de um cliente para um objeto remoto. Channels so objetos que transportam mensagens entre aplicaes remotas e so responsveis por "escutar mensagens remotas. Antes de processar essas mensagens, um Channel pode enviar a mensagem para uma cadeia de objetos Channel Sin0 (receptores). Cada Sin0 na cadeia recebe a mensagem, efetua operaes especficas e repassa para o prximo Sin0. Existem dois tipos de Channels no .NET: EttpChannel < namespace System.4%ntime.4emotin/.Channels.Ettp; $cpChannel < namespace System.4%ntime.4emotin/.Channels.$cp. Formatters (formatadores) so responsveis por codificar/decodificar mensagens para transmisso atravs de um Channel. Existem dois tipos de Formatters no .NET: SoapFormatter; BinaryFormatter. O interessante que podemos ter uma mesma aplicao usando canais diferentes com tipos de formatadores diferentes. Por exemplo, um objeto remoto por ser ativado tanto por $cpHBinary, o que mais rpido em uma rede interna, quanto por EttpHSoap atravs da internet. Em nosso exemplo, declaramos uma varivel do tipo EttpChannel. O construtor da classe recebe como parmetro o nmero da porta a ser utilizada, nesse caso usamos 8888. C*anne#Ser)%(es ChannelSer)ices, do namespace System.4%ntime.4emotin/.Channels. uma classe composta inteiramente de membros estticos, de forma que no precisamos instanci-la para usar seus mtodos. Oferece mtodos para manipular objetos Channels, como registrar, desregistrar, obter uma lista de Channels registrados e assim por diante. Em nosso exemplo, usamos o mtodo 4e/isterChannel de ChannelSer)ices para regitrar o objeto cnl do tipo EttpChannel, criado na linha anterior. T%pos de at%)a!o de ob?etos Objetos remotos do tipo Marshal<By<4eference podem ser criados (instanciados) de duas formas: Servidor (Ser)er Acti)ation) - so registrados no .NET Remoting e so criadas no servidor somente quando necessrio. No so criados quando o objeto pro-y criado na mquina cliente, mas somente quando feita a primeira chamada de mtodo. Objetos servidores podem ser ativados de duas formas: o Sin/leton < somente um objeto reside na memria do servidor e todas as chamadas clientes a esse objeto so serializadas; o Sin/leCall G um novo objeto criado para cada solicitao cliente; Cliente (Client Acti)ation) - a criao de objetos no servidor comandada pela aplicao cliente. Re+ot%n-Con>%-urat%on 4emotin/Confi/%ration, do namespace System.4%ntime.4emotin/, uma importante classe destinada a manuteno e configurao de uma aplicao distribuda usando .NET Remoting. Possui somente mtodos estticos. Em nosso exemplo, chamamos o mtodo 4e/ister5ell6no2nSer)ice$ype, que registra em uma aplicao servidora um determinado tipo como sendo remoto, tornando-o assim, acessvel por outras aplicaes. Na prxima parte deste artigo, veremos como criar a aplicao cliente. Parte <<I< C#%entes para ap#%(a7es 8NET Re+ot%n- Neste artigo veremos como criar a aplicao cliente para o servidor criado na parte anterior deste curso. Cr%ando a ap#%(a!o (#%ente Inicie uma nova aplicao Windows Forms e salve todos os arquivos do projeto em uma pasta chamada Client, dando o nome de Client.dpr ao projeto. Coloque no formulrio principal trs $e-tBo-es. trs Labels. dois B%ttons e um ListBo-, ajustando-os conforme mostrado na 5%-ura a seguir8
Figura. C#at> tela principal da aplica!o cliente Precisamos adicionar uma referncia no projeto servidor para o Assembly que defina o "contrato de comunicao entre servidor e cliente. Clique de direita no nome do projeto no (roect Mana/er e escolha Add 4eference. Na janela que aparece, vemos na lista superior apenas os Assemblies registrados no GAC. Clique no boto Bro2se e localiza o (ac0Intf.dll, selecione-a na lista e clique em OK. Declare os seguintes namespaces na clusula %ses da sesso interface:
Declare os seguintes objetos na sesso p%blic do formulrio:
cnl: Ptt0*hannel/ *hat!erver: I*hat!erver/
No evento Clic0 do boto Conectar digite o seguinte:
cnl := Ptt0*hannel.*reate/ *hannel!ervices.:egister*hannel+cnl./ *hat!erver := <ctivator.=et,b-ect+ty0e,5+I*hat.@ !ystem.!tring.'ormat+2htt0:KKF;GK*hat!erver2@&e3tBo3(.&e3t.. as I*hat/
No evento Clic0 do boto En)iar digite o seguinte:
var msg: *hat1sg/ begin if *hat!erver = nil then e3it/ msg := *hat1sg.*reate/ msg.Nome := &e3tBo3A.&e3t/ msg.1sg := &e3tBo3$.&e3t/ *hat!erver.7nvia1ensagem+1sg./
Coloque um $imer no formulrio, altere seu Enabled para $r%e. Inter)al para "1000 (1 segundo) e no seu evento $ic0 digite:
var 1sgs: *hat1sgs/ 1sg: *hat1sg/ begin if *hat!erver = nil then e3it/ #istBo3(.Items.*lear/ 1sgs := *hat!erver.:e5resh/ for 1sg in 1sgs do #istBo3(.Items.Insert+;@ !ystem.!tring.'ormat+ 2F;G[F(G] FAG2@[1sg.DataPora.&o!tring+2t2.@1sg.Nome@1sg.1sg]../ Testando o C*at Para testar o chat, executamos primeiro a aplicao servidora e a mantemos ativa. A seguir, basta executar n instncias da aplicao cliente, indiciando no primero $e-tBo- o nome e porta do servidor remoto. Digite seu nic0, clique no boto Conectar e envie mensagens. A 5%-ura a seguir mostra o chat em execuo.
Figura. =m c#at usando ob-etos distribu&dos e .N/" Remoting Nota: para facilitar o exemplo e focar o exerccio no .NET Remoting, no implementamos algumas funcionalidades bsicas como rolagem das mensagens, de forma que a ordem fica de cima para baixo, conforme recebido. Alm disso, o mtodo 4efresh retorna todas as mensagens a cada n segundos. Uma sugesto implementar o mtodo para retornar somente as mensagens novas.
Parte <<< 8NET Re+ot%n- (o+ ban(o de dados A ltima parte deste curso mostrar como criar aplicaes multicamadas no Delphi 2005 / 2006 que faro acesso a banco de dados. Ar.u%tetura de ap#%(a7es d%str%buJdas (o+ BDP e 8NET Re+ot%n- A 5%-ura a seguir mostra a arquitetura de uma soluo usando as tecnologias do exemplo. Nela, podemos identificar as seguinte camadas: Banco de Dados - armazena os dados da aplicao, podendo ser qualquer SGDB compatvel com ADO.NET ou BDP (Oracle, DB2, InterBase, Firebird, MySQL etc.); Ser)idor de Aplica>Io - camada onde residem as regras de acesso, incluindo DataAdapters para consultas e atualizaes, alm de mtodos especficos para processamento de regras de negcio; Cliente - faz acesso ao servidor de aplicao usando .NET Remoting. O acesso pode ser feito via HTTP ou TCP. O tipo do cliente pode ser Windows Forms ou Web Forms. No caso de ser ASP.NET Web Forms, teremos uma quarta camada na arquitetura, o browser.
Figura. 2r,uitetura de uma aplica!o multicamadas A 5%-ura a seguir mostra onde cada um dos novos componentes do BDP deve ser utilizado.
Figura. Componentes 3D8 para cria!o de aplica.es multicamadas Cr%ando o ser)%dor Crie uma aplicao do tipo 5indo2s Forms. A partir do Data E-plorer, arraste a conexo Employee, para criar um BdpConnection previamente configurado. Arraste tambm a tabela Employee para criar um BdpDataAdapter. Colocamos um DataSync e na sua propriedade (ro)iders adicione um item, conforme mostrado na 5%-ura a seguir.
Figura. 2dicionando os Data2dapters na propriedade 8roviders do DataS0nc Coloque um 4emoteSer)er, configure seu Channel$ype ($cp ou Ettp), a porta, o DataSync (apontando para o DataSync") e A%toStart para $r%e. A partir da, os DataAdapters podem ser "enxergados por uma aplicao remota. Fazendo uma analogia com o DataSnap, o DataSync nesse caso funciona como um DataSet(ro)ider, que prov acesso remoto aos DataAdapters (que so semelhantes s Q%eries da VCL). A 5%-ura a seguir mostra o formulrio do servidor.
Figura. Servidor de aplica!o Clique em 4%nC4%n 5itho%t Deb%//in/ e mantenha o servidor em execuo. Cr%ando u+ (#%ente C%ndo"s 5or+s Inicie uma aplicao do tipo Windows Forms e coloque no formulrio principal um 4emoteConnection para conectar ao servidor anterior. Configure as propriedades Channel$ype, (ort. (ro)ider$ype de acordo com os valores configurados no servidor. Em URI 4emoteSer)er". Veja a 5%-ura.
Figura. Configura!o do RemoteConnection
Coloque no formulrio um DataSet. Para "enxergar os DataAdapters do servidor referenciados pelo DataSync, coloque um DataE%b e configure seu Data(ort para 4emoteConnection" e aponte sua propriedade DataSet para DataSet". Ative o componente. Observe que o DataSet ser preenchido nesse momento com as tabelas do servidor (propriedade $ables). Coloque um Data!rid e configure seu DataSo%rce e DataMember. Coloque um B%tton e no seu evento Clic0 digite:
procedure &>in'orm(.B)tton(_*lic+sender: !ystem.,b-ect/ e: !ystem.7vent<rgs./ begin DataP)b(.<00ly*hanges/ end/
Veja a aplicao em execuo na 5%-ura a seguir8
Figura. Cliente 7indoBs Forms para o servidor de aplica!o Cr%ando u+ (#%ente ASP8NET Ceb 5or+s Vamos agora criar uma interface Web para nossa soluo multicamadas. Inicie uma nova aplicao ASP.NET Web Forms e configure a conexo ao servidor. Os componentes utilizados e as configuraes que devem ser feitas so exatamente as mesmas que da aplicao Windows Forms, tanto que no repetirei aqui. Alm dos componentes de conexo, voc precisa de um DB5ebDataSo%rce apontando para o DataSet. Usamos um DB5eb!rid e um DB5eb*a)i/ator, configurando as propriedades DBDataSo%rce e $able*ame. No evento ,nA%toApply4e9%ests e ,nApplyChan/e4e9%ests do DB5ebDataSo%rce digite:
DataP)b(.<00ly*hanges/
Veja na 5%-ura a seguir como ficou a aplicao.
Figura. Cliente 7eb Forms para o servidor de aplica!o Passando parK+etros para (onsu#tas re+otas muito simples passar um parmetro para um BdpDataAdapter que esteje parametrizado. Em nosso exemplo, altere a propriedade SelectCommand.Command$e-t do BdpDataAdapter para:
!7#7*& 8 ':,1 71P#,J77 where 71P_N, = Y
Adicione o parmetro na propriedade (arameters do SelectCommand, dando o (arameter*ame e So%rceCol%mn para Emp7*o. Em Bdp$ype escolha IntJ'. Rode a aplicao. Na aplicao cliente Windows Forms, coloque um B%tton e um $e-tBo-. No evento Clic0 do boto digite:
procedure &>in'orm(.B)tton(_*lic(+sender: !ystem.,b-ect/ e: !ystem.7vent<rgs./ var da: Bd0Data<da0ter/ begin da := DataP)b(.Providers[;].Data<da0ter as Bd0Data<da0ter/ da.!elect*ommand.Parameters[;].9al)e := &e3tBo3(.&e3t/ DataP)b(.:e5resh/ end/
No cdigo anterior, obtemos uma refer:ncia para o BdpDataAdapter remoto fazendo um typecast no primeiro item da coleo (ro)iders. j que s temos um DataAdapter no servidor. A seguir, passamos normalmente o valor para o parmetro, como se o Adapter fosse local. O mtodo 4efresh do DataE%b atualiza a consulta (5%-ura).
Figura. 8assando par'metros remotamente Do"n#oad Voc pode fazer download de todos os exemplos deste curso a partir do endereo http#HHcc.borland.comHA%thor.asp-KID&'''LLM. preciso fazer o cadastro na BDN, que gratuito e pode ser feito a partir do endereo http://bdn.borland.com
dbExpress$ DataSnap e C#%entDataSet& T'(n%(as A)anadas Para mais informaes sobre acesso a dados no Delphi e tcnicas avanadas, sugiro a leitura do meu livro, "Delphi: Programao para Banco de Dados e Web, como apoio para o aprendizado das tecnologias. Na obra mostro vrias tcnicas introdutrios e avanadas de desenvolvimento com ClientDataSet, dbExpress e DataSnap (multicamadas, incluindo SOAP e COM+). Para mais informaes, consulte o link http#HH222.cl%bedelphi.netH/%inther