Você está na página 1de 85

Sobre o Autor

Guinther Pauli DelphiMan tem mais de 10 anos


de experincia em Delphi, autor de mais de 100 artigos publicados na rea e do livro Delphi Programao para Banco de Dados e Web. Bacharel em Sistemas de Informao pelo Centro Universitrio Franciscano (Santa Maria RS). certificado oficial Delphi Advanced pela Borland dos Estados Unidos, Delphi Web Development Certified e
Kylix Product Certified. J ministrou palestras para
mais de 5 mil pessoas em todo o pas, incluindo
Borland Conference e ClubeDelphi Tech Day.
editor geral da revista ClubeDelphi
(www.clubedelphi.net) e co-editor da Revista Web
Mobile Magazine (www.portalwebmobile.com.br). Pode ser contatado pelo
endereo guinther_pauli@hotmail.com ou guinther@clubedelphi.net.

Download
Todos os cdigos-fonte deste curso podem ser obtidos em cc.borland.com/
ccweb.exe/author?authorid=222668

1. Introduo
Este curso mostrar como utilizar os componentes do ADO.NET e BDP
para acesso a banco de dados usando o Delphi 8. Nesta primeira parte, faremos uma introduo ao ADO.NET, mostrando seus objetivos, arquitetura e
principais componentes. Depois, veremos na prtica como utilizar cada um dos
componentes do ADO.NET e BDP no Delphi 8, em aplicaes Windows Forms,
Web Forms e Web Services. Os exemplos que mostrarei neste curso so semelhantes ao que apresentei durante minha palestra na III Borland Conference.
O cdigo-fonte pode ser baixado no endereo cc.borland.com/cc/ccweb.exe/
author?authorid=222668.

Introduo ao ADO.NET
O ADO.NET a tecnologia de acesso a dados no .NET Framework. Sua
arquitetura elegante e bem definida oferece inmeros benefcios:
Interoperabilidade, Escalabilidade, Produtividade e Performance. O ADO.NET
uma evoluo do ADO (Active Data Objects) e totalmente escrito com
cdigo gerenciado (Managed Code). O ADO.NET uma evoluo do modelo
cliente/servidor, projetada especialmente para a construo de aplicaes
escalveis, distribudas e para Web.
Costumo dizer que o ADO.NET possui muitas caractersticas que a Borland
j havia incorporado tecnologia DataSnap. De fato, ambos os frameworks
foram construdos para suportar os mesmos tipos de aplicao. Com isso, se
voc j est acostumado a desenvolver aplicaes DataSnap ou mesmo client /
server (com dbExpress / DataSetProvider / ClientDataSet) no Delphi, no ter
maiores dificuldades em se adaptar ao novo modelo.
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.1
Importante
Nenhuma parte deste curso,
sem autorizao prvia por
escrito do autor e da editora,
poder ser reproduzida, copiada ou transmitida, sejam
quais forem os meios empregados: eletrnicos, fotogrficos, gravao ou quaisquer
outros. Todos os direitos reservados e protegidos pela lei
5.988 de 14/12/73.h

Vejamos a seguir uma descrio dos principais objetivos da arquitetura:


Interoperabilidade
* Uso de XML para intercmbio de dados ao invs do COM (ADO);
* Trfego de dados atravs do HTTP (minimizando problemas com
Firewalls);
Escalabilidade
* Evoluo do ADO e modelo cliente / servidor;
* Baseado em DataSets desconectados e no modelo de objetos distribudos;
Performance e robustez
* Projetado para grandes aplicaes corporativas e Web;
Produtividade
* Possui um rico conjunto de classes e interfaces com funes bem definidas;
* So vrias linguagens e ferramentas de desenvolvimento que oferecem
suporte ao ADO.NET, de forma que h uma curva de aprendizado muito pequena ao se trocar de linguagem;

Componentes do ADO.NET
As classes do ADO.NET esto divididas em dois grandes grupos: Managed
Provides (provedores gerenciados) e Content Components (componentes de
contedo).
Os componentes do grupo Managed Providers so responsveis pelo Acesso
a Dados, e incluem classes para conexo, transaes, execuo de comandos
e leitura de dados. O segundo grupo engloba os componentes que manipulam
os dados em memria, como DataSet, DataTable, DataRow, DataColumn etc.
No ADO.NET, os Providers so responsveis pelo acesso a dados. Um
Provider um conjunto de componentes que implementam as interfaces bsicas do ADO.NET, a saber:
IDbConnection - define mtodos e propriedades para conexo a uma fonte
de dados;
IDbDataAdapter - define mtodos e propriedades para obteno e atualizaes de dados;

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.2

IDbTransaction - define mtodos e propriedades para gerenciamento de


transaes;
IDataReader - define mtodos e propriedades para manipulao / leitura
de um cursor de dados;
IDbCommand define mtodos e propriedades para execuo de comandos SQL no BD:
Observe que, obviamente, as interfaces apenas definem seu comportamento. A partir da, qualquer fabricante de um BD pode criar componentes que
implementem essas interfaces para permitir acesso ao seu SGBD. Com isso, o
ADO.NET (por ser uma arquitetura aberta) podem facilmente ser estendido.
Como todos os componentes seguem o mesmo padro (j que implementam as
mesmas interfaces), possvel utilizar esses componentes de forma bastante
semelhante em aplicaes Windows Forms, Web Forms e Web Services. Ao
conjunto de componentes que implementam essas interfaces dado o nome de
Provider.
O .NET Framework 1.1 j distribudo com quatro Providers nativos:
SQL Provider para acesso ao SQL Server;
OleDB Provider para acesso a fontes de dados que possuam um driver
OleDB;
ODBC Provider - para acesso a fontes de dados que possuam um driver
ODBC;
Oracle Provider para acesso ao Oracle.
Ou seja, se voc precisar acessar o SQL Server, por exemplo, utilizar as
classes do primeiro Provider da lista anterior. Nesse caso: SqlConnection,
SqlDataAdapter, SqlTransaction, SqlDataReader, SqlCommand etc. Se escolher o Oracle, ento temos: OracleConnection, OracleDataAdapter,
OracleTransaction, OracleDataReader, OracleCommand etc.
Existem diversos outros Providers para ADO.NET disponibilizados na Web,
por exemplo, para acesso ao Firebird, MySQL etc.
O que Borland Data Provider (BDP)?
Usar componentes diferentes para bancos diferentes (a menos que voc use
uma conexo OleDb ou ODBC) pode soar estranho para os desenvolvedores
Delphi, j que estamos acostumados a utilizar um mesmo conjunto de componentes para acesso a uma diversidade de servidores de BD. De fato, o dbExpress,
por exemplo, permite a utilizao de um mesmo conjunto de componentes para
acessar o Interbase, Oracle, DB2, MySQL etc.

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.3

Sabendo disso, a Borland criou seu prprio Provider para ADO.NET,


implementando as interfaces listadas anteriormente. A grande diferena que a
implementao no est vinculada a um nico BD, mas a um Driver (algo semelhante ao que faz o dbExpress). Com isso, para acessar diferentes BD voc no
precisar usar diferentes componentes, apenas trocar alguns parmetros de
conexo.
Esse Provider construdo pela Borland distribudo com o Delphi 8 e
C#Builder, e recebeu o nome de Borland Data Provider (BDP). Uma outra
vantagem do BDP suportar o recurso de Live-Data, de forma que voc pode
visualizar dados do BD em controles de tela mesmo em tempo design. Incrivelmente, esse recurso no est presente nos demais Providers ou outras ferramentas de desenvolvimento, como o VS.NET.
Os componentes do BDP podem ser vistos na figura a seguir:

Neste curso daremos especial ateno ao BDP. Mas lembre-se, como todos os Providers seguem o mesmo padro (implementam as mesmas interfaces),
a maioria das tcnicas que mostrarei neste curso usando o BDP podem ser
facilmente adaptadas a outros Providers. Ou seja, quando for apresentado um
recurso do BdpCommand, por exemplo, saiba que o mesmo pode ser utilizado
com o OracleCommand, SqlCommand, OleDbCommand etc. claro, o BDP
possui algumas exclusividades, que indicarei no texto quando a mesma no
estiver presente nos demais Providers.
O segundo grupo de componentes do ADO.NET so responsveis pela
cache e manipulao de dados na aplicao cliente. O principal componente
desse grupo (e diria da arquitetura do ADO.NET como um todo) o DataSet,
que uma coleo de objetos DataTable, algo semelhante ao ClientDataSet
para quem usa a VCL. O DataSet independe da fonte de dados, e pode ser
utilizado com qualquer um dos Providers listados anteriormente (o que tambm
se assemelha ao ClientDataSet da VCL, que pode ser usado como o dbExpress,
BDE, ADO, IBX etc). Faremos muitos exemplos demonstrando tcnicas avanadas do uso deste componente, e o estudaremos em detalhes futuramente.

Arquitetura

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.4

A figura a seguir mostra a arquitetura de uma aplicao distribuda usando


ADO.NET. Observe que, por usar XML para intercmbio de dados, voc
pode facilmente trocar dados atravs da Internet. A aplicao cliente pode ser
construda usando Windows Forms ou Web Forms:

.5
Nota Lembre-se que nosso foco principal a utilizao dos componentes do
BDP, porm, a maioria das
tcnicas apresentadas pode
ser facilmente aplicada aos
demais providers.

Comparativo ADO.NET x VCL (dbExpress / BDE)


Acredito que a maioria dos leitores j utiliza o dbExpress ou o BDE para
construir aplicaes de acesso a BD no Delphi. Dessa forma, construi uma
pequena tabela (abaixo) que mostra um paralelo entre as classes do ADO.NET
(providers BDP e SQL) e da VCL, para que voc possa se situar melhor:
A D O .N E T
BDP
Bd p C onnectio n
B d p C o mmand
Bd p D ataRead er
B d p D ataA d ap ter

S Q L M a na g e d
S qlC o nnectio n
S Q lC o mmand
S qlD ataRead er
S q lD ataA d ap ter

Bd p Transactio n

S qlTransactio n

D a ta S e t

VC L
dbE x pre s s
S q lC o nnectio n
* IS Q LC o mmnand
S Q LD ataS et
S Q LD ataS et +
D ataS etP ro vid er
Transa es gerenciad as
pelo S Q LC onnectio n

BDE
D ataB ase
Q uery
Q uery +
D ataS etP r
Transa e

gerenciad a
C o le o de v rio s C lie ntD a ta S e

* No possui um componente equivalente direto

2. Conexes com BdpConnection


Nesta segunda parte do curso, comearemos a estudar cada um dos componentes utilizados em aplicaes de banco de dados usando ADO.NET e
BDP. Neste artigo conheceremos o componente BdpConnection, suas principais propriedades, mtodos e eventos.
O BdpConnection responsvel por estabelecer uma conexo com o banco de dados. Suas principais propriedades so ConnectionString, que contm
as informaes necessrias para conexo com o BD, e ConnectionOptions ,
que contm os parmetros opcionais da conexo, como nvel de isolamento de
transao, LoginPrompt etc. Seus principais mtodos so Open e Close. Seu
evento StateChange disparado quando alterado e estado da conexo
(ConnectionState.Open ou ConnectionState.Closed).

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

Dica O componente
BdpConnection do BDP
equivale aos seguintes da
VCL:
Database (para quem usa
BDE)
SQLConnection (dbExpress)
ADOConnection (ADO)
IBConnection (IBX)

Criando uma conexo


Faremos agora uma conexo ao banco de dados employee que distribudo com os demos do Interbase. Para construir este exemplo, necessrio que
voc tenha instalado o Interbase 7.1
No Delphi 8, v at o Data Explorer localizado ao lado do Project Manager
e Model View. O Data Explorer permite que voc explore os bancos de
dados configurados para serem acessados pelo BDP, permitindo a visualizao
de tabelas, view, ndices etc.
D um clique de direita sobre o item Interbase e escolha Add New
Connection:

Em Provider Name escolha Interbase e em ConnectionName digite


EMPLOYEE:

Aperte Ok. A seguir, no Data Explorer, expanda o item Interbase e d um


clique de direita sobre a conexo EMPLOYEE, escolhendo a opo Modify
Connection.

Ser mostrado o editor de conexes do BDP, o Connections Editor, que


semelhante ao editor de conexes do dbExpress do Delphi 7.
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.6

Configura opo Database para apontar para o banco de dados


employee.gdb, como mostrado a seguir (lembre-se de incluir localhost antes do
caminho do banco):

Clique em Test para verificar se os parmetros de conexo foram configurados


corretamente:

Clique em Ok para confirmar as alteraes.

As informaes sobre as conexes criadas com o BDP ficam salvas em um


arquivo chamado bdpconnections.xml, localizado por padro no diretrio Bin
do Delphi. Veja o contedo deste arquivo na figura a seguir:

Este arquivo semelhante ao dbxconnections.ini, utilizado pelo dbExpress.

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.7

Um exemplo prtico
Clique em File|New|Windows Forms Application. Arraste at o formulrio o
componente BdpConnection da categoria Borland Data Provider. Observe que
em uma aplicao Windows Forms os componentes no-visuais so
posicionados abaixo do designer.
Selecione o BdpConnection1 e no Object Inspector escolha EMPLOYEE
na propriedade ConnectionString:

Neste momento, o Delphi atribui para a propriedade ConnectionString uma


srie de parmetros no estilo parmetro=valor, separados por ;
Para este exemplo, nossa ConnectionString ficou como:
database=localhost:C:\Borland\InterBase\examples\database\employee.gdb;
assembly=Borland.Data.Interbase, Version=1.5.1.0, Culture=neutral,
PublicKeyToken=91d62ebb5b0d1b1b;vendorclient=gds32.dll;provider=Interbase;
username=sysdba;password=masterkey;

E ConnectionOptions ficou como:


waitonlocks=False;commitretain=False;sqldialect=3;
transaction isolation=ReadCom
mitted;servercharset=;rolename=myrole;

Voc pode verificar o cdigo gerado para o componente no mtodo


InitializeComponent do formulrio. Lembre-se que o .NET Framework no
possui o conceito de persistncia de objetos em arquivos DFM como faz a
VCL, de forma que todas as propriedades dos componentes so inicializadas
nesse mtodo.
No formulrio principal, coloque dois componentes TextBox, dois Buttons e
uma StaturBar (todos da categoria Windows Forms). Configure a propriedade
ReadOnly e MultLine dos TextBoxes para True.
Seu formulrio deve estar semelhante ao mostrado a seguir (configure tambm o Text dos Buttons):
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.8
Dica: se voc arrastar a conexo Employee do Data
Explorer at o formulrio,
ser criado automaticamente
um BdpConnection, j configurado.

.9

No evento Click do boto Load digite:


procedure TWinForm1.Button1_Click(sender: System.Object; e: System.EventArgs);
begin
BdpConnection1.Open;
end;

Isso abrir a conexo com o banco de dados, usando o mtodo Open do


BdpConnection.
Para fechar a conexo, no evento Click do boto Close digite:
procedure TWinForm1.Button2_Click(sender: System.Object; e: System.EventArgs);
begin
BdpConnection1.Close;
end;

Q
uando o estado de uma conexo muda, o evento StateChange do
BdpConnection disparado. Insira um manipulador para este evento e digite o
seguinte:
procedure TWinForm1.BdpConnection1_StateChange(
sender: System.Object; e: System.Data.StateChangeEventArgs);
begin
case BdpConnection1.State of
ConnectionState.Open : StatusBar1.Text := Connected to + BdpConnection1.Database;
ConnectionState.Closed : StatusBar1.Text := Disconnected;
end;
end;

Aqui testamos o valor da propriedade State do BdpConnection, que do


tipo enumerado ConnectionState. De acordo com o estado, exibimos uma informao na propriedade Text da StatusBar.
A propriedade Database do BdpConnection (somente-leitura) retorna o
nome do banco de dados a atualmente conectado.
No ebento Load do formulrio digite:
procedure TWinForm1.TWinForm1_Load(sender: System.Object; e: System.EventArgs);
begin
TextBox1.Text := BdpConnection1.ConnectionString;
TextBox2.Text := BdpConnection1.ConnectionOptions;
end;

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

Isso configura os TextBoxes com os atuais valores das propriedades


ConnectionString e ConnectionOptions.
Execute a aplicao e teste a conexo:

Uma situao bastante comum voc ter que alterar os parmetros de conexo em tempo de execuo, quando sua aplicao for distribuda. Isso pode
acontecer devido ao caminho do banco de dados ser diferente quele configurado durante a fase de desenvolvimento, por exemplo. Poderamos inserir cdigo para ler em tempo de execuo as entradas do arquivo bdpconnections.xml,
e distribuir esse arquivo junto com a aplicao. O problema que esse arquivo
exclusivo do BDP e a tcnica no poderia ser utilizada para outros providers.
O .NET Framework oferece um mecanismo bastante interessante para permite a configurao dinmica de propriedades de componentes em tempo de
execuo, recurso conhecido como Dynamic Proprierties.
Vejamos na prtica um exemplo de utilizao do recurso. Selecione o
BdpConnection
e
abra
o
editor
da
propriedade
DynamicProprierties.ConnectionString, e marque a opo mostrada a seguir:

Com isso, o valor da propriedade ser armazenado em um arquivo XML


chamado app.config que adicionado ao projeto:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.10

Ou seja, agora voc pode alterar qualquer parmetro da ConnectionString


em tempo de execuo, sem recompilar a aplicao:

.11

Para fazer o BdpConnection ler o valor da propriedade a partir do arquivo


XML, adicione o seguinte cdigo ao evento Load do formulrio:
BdpConnection1.ConnectionString :=
System.Configuration.ConfigurationSettings.AppSettings.Get(
BdpConnection1.ConnectionString);

3. BdpCommand e BdpDataReader
Nesta terceira parte do curso conheceremos o componente BdpCommand
e a classe BdpDataReader do BDP.
O BdpCommand permite a execuo de comandos SQL ou
StoredProcedures no servidor. Este componente no possui um equivalente
direto nas tecnologias de acesso da VCL (dbExpress, BDE etc.).
No dbExpress, quando voc usa o componente SQLQuery e acionada seu
mtodo Open, chamado internamente um mtodo da interface ISQLCommand
e ISQLCursor para tratar a abertura da consulta. Quando voc precisa executar um comando de atualizao (Insert, Update ou Delete) diretamente, pode
usar um SQLQuery e chamar seu mtodo ExecSQL. No BDP temos um componente especializado para a execuo de comandos, o BdpCommand.
Clique em File|New|Windows Forms Application e arraste at o formulrio
um BdpConnection e um BdpCommand. Configure a propriedade
ConnectionString do BdpConnection para apontar para Employee. Aponte a
propriedade Connection do BdpCommand1 para BdpConnection1.
Vamos analisa o componente BdpCommand. Sua propriedade
CommandType especifica o tipo de comando, que pode ser Text,
StoredProcedure ou TableDirect. Essa ltima opo permite que voc acessar
uma tabela apenas pelo Nome (ex. EMPLOYEE), o que internamente transformando em um Select * from Tabela.
CommandText a instruo a ser executada, ou o nome da tabela ou Stored
Procedure. O mtodo ExecuteNonQuery executa o comando, quando no
retornar um resultset (ex. UPDATE,DELETE ou INSERT). Chame
ExecuteReader quando o comando retornar um cursor (ex. SELECT), o retorno da funo um DataReader que pode ser utilizado para varrer o resultset.
ExecuteScalar pode ser usado para retornar apenas o primeiro campo do primeiro registro (ideal para consultas do tipo SELECT MAX, COUNT ...). Continuando o exemplo, vamos ver como utilizar na prtica essas propriedades e
mtodos.
Coloque no formulrio dois ComboBoxes, trs Buttons, um ListView, dois
Labels e uma StatusBar. Seu formulrio deve estar semelhante ao mostrado a
seguir:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

Nota: o uso de
CommandText
no
BdpCommand algo semelhante ao que j existia para o
ClientDataSet
ou
SQLDataSet do dbExpress.

.12

Nossa aplicao vai permitir a configurao das propriedades CommandText


e CommandType em tempo de execuo. Trs botes fazem as chamadas aos
mtodos do BdpCommand, para deixar mais claro, configurei o Text de cada
boto para representar o mtodo que chamam.
No evento Load do formulrio digite o seguinte:
procedure WinForm.WinForm_Load(sender: System.Object; e: System.EventArgs);
var
i: integer;
ar: &array;
begin
BdpConnection1.Open;
{ preenhe Combo com CommandTypes usando Reflection }
ar := Enum.GetValues(TypeOf(CommandType));
for i := 0 to pred(ar.Length) do
ComboBox2.Items.Add(ar.GetValue(i));
ComboBox2.Text := ar.GetValue(0).ToString;
end;

O cdigo anterior usa reflexo para preencher o ComboBox2 com os possveis valores para a propriedade CommandType do BdpCommand, que pode
ser Text, StoredProcedure ou TableDirect:

No evento SelectedIndexChanged do ComboBox2 digite o seguinte:


procedure WinForm.ComboBox2_SelectedIndexChanged(
sender: System.Object; e: System.EventArgs);
begin
BdpCommand1.CommandType :=
CommandType(Enum.Parse(TypeOf(CommandType),ComboBox2.Text));
end;

Isso configura o CommandType de acordo com o item selecionado.


Insira o seguinte cdigo no manipulador do evento Click do boto
ExecuteNonQuery:
procedure WinForm.Button1_Click(sender: System.Object;
e: System.EventArgs);
var
r: integer;
begin

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

ListView1.Visible := False;
BdpCommand1.CommandText := ComboBox1.Text;
r := BdpCommand1.ExecuteNonQuery();
StatusBar1.Text := r.ToString + registros afetados;
end;

Aqui chamamos o mtodo ExecuteNonQuery do BdpCommand, que retorno o nmero de registros afetados por um Update, Delete ou Insert (exibimos esse valor na StatusBar).
Se o comando SQL retornar um conjunto de dados (um cursor), como em
uma instruo Select, precisamos chamar o mtodo ExecuteReader, que retorna
um BdpDataReader. Utilizamos ento esse objeto para varrer os registros
retornados. A leitura com um BdpDataReader forward-only e read-only, o
que pode aumentar a velocidade e escalabilidade das aplicaes. No entanto,
no feita a cache de dados (veremos mais sobre isso ao estudarmos o
BdpDataAdapter e DataSet). O BdpDataReader nesse caso semelhante a
um DataSet unidirecional do dbExpress, como o SQLDataSet.
A seguir, varremos o BdpDataReader e para cada registro lido informamos
os valores dos campos em um ListView. Isso feito no evento Click do boto
ExecuteReader:
procedure WinForm.Button2_Click(sender: System.Object; e: System.EventArgs);
var
rd: BdpDataReader;
i: integer;
LvItem: ListViewItem;
r: integer;
begin
ListView1.Clear;
ListView1.Visible := True;
BdpCommand1.CommandText := ComboBox1.Text;
rd := BdpCommand1.ExecuteReader();
{ adiciona colunas do ListView conforme colunas do DataReader }
for i := 0 to pred(rd.FieldCount) do
ListView1.Columns.Add(rd.GetName(i),80,HorizontalAlignment.Left);
{ Percorre o DataReader - leitura sequencial (forward - only)
e preenche os itens do ListView }
while rd.Read do
begin
lvItem := ListViewItem.Create;
ListView1.Items.add(LvItem);
for i := 0 to pred(rd.FieldCount) do
if i = 0 then
lvItem.Text := rd[rd.GetName(i)].ToString
else
LvItem.SubItems.Add(rd[rd.GetName(i)].ToString);
inc(r);
end;
StatusBar1.Text := r.ToString +
registros retornados;
rd.Close;
end;

E finalmente, insira o seguinte cdigo no manipulador do evento Click do


boto ExecuteScalar:
procedure WinForm.Button3_Click(sender: System.Object; e:
System.EventArgs);
var
v: &object;
begin
ListView1.Clear;
ListView1.Visible := True;
BdpCommand1.CommandText := ComboBox1.Text;
v := BdpCommand1.ExecuteScalar;
ListView1.Columns.Add(VALOR,100,HorizontalAlignment.Left);
ListView1.Items.Add(v.ToString);

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.13

StatusBar1.Text := ;
end;

ExecuteScalar pode ser utilizado para retornar apenas o primeiro campo do


primeiro registro de uma consulta, o que pode aumentar a performance de consultas que usam funes como SELECT COUNT, MAX, MIN etc.
Execute a aplicao e faa os testes conforme mostrado a seguir:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.14

4. BdpDataAdapter
Vimos na parte III do curso que um BdpCommand permite a execuo de
comandos SQL no banco de dados. Geralmente, ao manipularmos uma tabela
no BD atravs de uma aplicao, precisamos efetuar quatro operaes bsicas:
consulta (Select), atualizao (Update), excluso (Delete) e insero (Insert).
Dessa forma, o BdpDataAdapter abstrai em uma nica estrutura quatro
BdpCommands internos: SelectCommand, DeleteCommand, UpdateCommand
e InsertCommand, que permitem manipular os dados do BD. Alm disso, seu
mtodo Fill preenche um DataSet com os dados da consulta (o BDP permite
ainda o uso da propriedade Active como alternativa).

O mtodo Update do BdpDataAdater atualiza os dados do BD com base


nas alteraes feitas no DataSet. AutoUpdate , semelhante ao Update, atualiza
os dados no BD, sem no entanto usar comandos de atualizao explcitos (eles
so gerados por um BdpCommandBuilder). O AutoUpdate um recurso exclusivo do BDP, e semelhante ao que faz o DataSetProvider no DataSet (gerando automaticamente as instrues de atualizao).
Dessa forma, o BdpDataAdapter funciona como uma ponte entre os dados armazenados no banco de dados e a cache da aplicao, gerenciando a
execuo de consultas e atualizaes. Ele possui funcionalidade semelhante ao
SQLDataSet + DataSetProvider do dbExpress / DataSnap.
Faremos agora um exemplo prtico que demonstra a utilizao do componente. Inicie uma nova aplicao do tipo Windows Forms Application. Expanda a conexo Employee no Data Explorer e arraste a tabela Country para o
designer. Isso cria um BdpConnection e um BdpDataAdapter.
Clique de direita sobre o BdpDataAdapter e escolha a opo Configure
Data Adapter. Observe os comandos SQL gerados para o componente:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.15
Nota: conheceremos o
DataSet em detalhes em vrios exemplos nos captulos a
seguir deste curso. Por enquanto, importante saber
apenas que o DataSet permite a manipulao dos dados
em memria, desconectado
do banco de dados, semelhante ao que o ClientDataSet
para o DataSnap.

Essas instrues ficam armazenadas nas propriedades SelectCommand,


DeleteCommand, UpdateCommand e InsertCommand do BdpDataAdapter.
Elas j foram configuradas quando arrastamos a tabela Country para o Designer.
O processo manual de gerao consiste em escolher uma conexo na opo
Connection, marcar quais os comandos devem ser gerados (Select, Update,
Insert e Delete) e clicar no boto Generate SQL. A opo Optimize faz com
que somente a chave-primria ta tabela seja utilizada em instrues de atualizao.
Clicando em PreviewData voc pode visualizar os dados (clique em Refresh):

Na opo DataSet marque o seguinte e clique em Ok:

Isso adiciona um componente DataSet ao Designer. Observe que o


BdpDataAdapter aponte para esse componente atravs da propriedade DataSet,
e que podemos ativ-lo atravs da propriedade Active (um recurso exclusivo
do BDP conhecido como live-data).

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.16

.17

Vamos configurar os controles do formulrio. Coloque um DataGrid, trs


Buttons (btUpdate, btAutoUpdate e btAbrir) , dois TextBoxes (tbStart
e tbMax), um CheckBox (cbContinue) e dois Labels. Ajuste-os no formulrios conforme mostrado a seguir (utilize a propriedade Text para configurar os
textos dos componentes):

No evento Click do boto Update digite o seguinte:


procedure TWinForm.btUpdate_Click(
sender: System.Object; e: System.EventArgs);
begin
BdpDataAdapter1.Update(DataSet1);
end;

Esse comando atualiza os dados usando as instrues de atualizao configuradas.


No evento Click do boto AutoUpdate digite o seguinte:
procedure TWinForm.btAutoUpdate_Click(
sender: System.Object; e: System.EventArgs);
begin
BdpDataAdapter1.AutoUpdate;
end;

Esse comando atualiza os dados usando um BdpCommnandBuilder, uma


alternativa ao uso do Update. Se estiver usando BDP, com certeza o AutoUpdate
mais prtico, porm no to flexvel. Aqui apresentei o uso dos dois mtodos
para voc ver como funcionam na prtica.
No evento Click do boto Abrir digite o seguinte:
procedure TWinForm.btAbair_Click(sender: System.Object;
e: System.EventArgs);

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

begin
BdpDataAdapter1.Active := False;
if tbStart.Text <> then
BdpDataAdapter1.StartRecord := Convert.ToInt32(tbStart.Text);
if tbMax.Text <> then
BdpDataAdapter1.MaxRecords := Convert.ToInt32(tbMax.Text);
BdpDataAdapter1.Active := True;
DataGrid1.DataSource := DataSet1.Tables[0];
BdpDataAdapter1.TableMappings.Clear;
BdpDataAdapter1.TableMappings.Add(Table,COUNTRY);
end;

Isso abre a consulta e configure o DataGrid para exibir os dados do primeiro DataTable do DataSet. Observe que configuramos as propriedades
StartRecord e MaxRecords do BdpDataAdapter para limitar o nmero de registros retornados pelo cursor. Voc pode, por exemplo, fazer cache apenas
dos registro 5 a 50. Esse recurso exclusivo do BDP.
No evento CheckedChange do CheckBox digite:
procedure TWinForm.cbContinue_CheckedChanged(sender: System.Object; e: System.EventArgs);
begin
BdpDataAdapter1.ContinueUpdateOnError := cbContinue.Checked;
end;

Isso configura a propriedade ContinueUpdateOnError do BdpDataAdapter.


Essa propriedade, um recurso exclusivo do BDP, especifica se o componente
deve continuar atualizando os registros em cache caso um erro acontea no
servidor de BD, por exemplo, uma violao de Foreign-Key. Esse recurso
semelhante ao que se consegue passando o valor -1 para o ApplyUpdates do
ClienDataSet em uma aplicao DataSnap.
No evento Load do formulrio digite o seguinte:
procedure TWinForm.TWinForm_Load(
sender: System.Object; e: System.EventArgs);
begin
BdpConnection1.Open;
end;

Execute a aplicao e faa alguns testes. Clique no boto Abrir para retornar
os dados da consulta e preencher o DataSet, bem como exibir os dados no
DataGrid:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.18

Atualize o campo Currency de alguns registros e clique no boto Update. A


seguir, faa o mesmo teste com o AutoUpdate. Volte para a IDE e limpe o
CommandText da propriedade UpdateCommand do BdpDataAdatpter. Execute a aplicao, altere alguns registros e repare que o Update deixa de funcionar, pois precisa que as instrues SQL estejam configuradas no
BdpDataAdapter. Verifique que o AutoUpdate continua funcionando, pois ele
usa um BdpCommandBuilder internamente para gerar as instrues de atualizao em tempo de execuo.
Um alternativa colocar um BdpCommandBuilder no formulrio e apontar
sua propriedade Adapter para o BdpDataAdapter. Com isso, voc poder
chamar normalmente o Update sem configurar as instrues de atualizao em
tempo de design. No caso do BDP isso no muito til devido existncia do
AutoUpdate, mas muito utilizado em outros Providers como SQL Server e
OleDb, por exemplo.
Agora marque a opo ContinueUpdateOnError e atualize o campo Country
de alguns pases, o que violar uma Foreign-Key no servidor:

Atualize os dados e verifique o BdpDataAdapter, mesmo aps a primeira


violao, tentou aplicar as alteraes nos demais registros. O DataGrid se encarrega de usar um ErrorProvider para exibir um cone com a mensagem de
erro gerada no BD:

5. Consultas parametrizadas com o BdpParameter


Nesta quinta parte do curso de acesso a dados no Delphi 8 for .NET conheceremos o BdpParameter, utilizado para construir consultas parametrizadas com
o BDP, o provider da Borland para o ADO.NET.

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.19

Consultas parametrizadas so um dos fundamentos da programao cliente


/ servidor e multicamadas, essencial que voc conhea como criar esse tipo
de consulta ao utilizar o ADO.NET, reduzindo trfego desnecessrio de dados
na rede a maximizando a performance da aplicao. Usando parmetros, voc
tem mais controle sobre quais registros devem ser deslocados do banco de
dados at a cache de dados na parte cliente da aplicao.
Criando a consulta parametrizada
Faremos agora um exemplo prtico que demonstra a utilizao do
BdpParameter. Inicie uma nova aplicao do tipo Windows Forms Application.
Expanda a conexo Employee no Data Explorer e arraste a tabela Customer
para o designer. Isso cria um BdpConnection e um BdpDataAdapter.

Clique de direita sobre o BdpDataAdapter e escolha a opo Configure


Data Adapter. Observe os comandos SQL gerados para o componente:
Observe que colocamos uma condio where na instruo select, indicando
que queremos retornar somente o customer cujo CUST_NO for igual ao
parmetro informado (um parmetro representado no BDP pelo sinal ?):
SELECT
CUST_NO, CUSTOMER, CONTACT_FIRST, CONTACT_LAST,
PHONE_NO, ADDRESS_LINE1, ADDRESS_LINE2, CITY,
STATE_PROVINCE, COUNTRY, POSTAL_CODE, ON_HOLD
FROM CUSTOMER
WHERE CUST_NO = ?

Clique na aba DataSet e gera o DataSet para o BdpDataAdapter, e clique


em Ok.
Configurando o parmetro
Com o BdpDataAdapter selecionado, clique sobre o smbolo [...] ao lado
da sua propriedade SelectCommand.Parameters.

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.20

.21

No editor que abrir, clique no boto Add para criar um novo BdpParameter,
e configure suas propriedades conforme mostrado na figura a seguir:

Clique em Ok para confirmar a criao do parmetro. Ative o


BdpDataAdapter (Active = True).
Configurando o formulrio principal
Coloque no formulrio principal da aplicao os seguintes componentes:
DataGrid, Button e TextBox. Configure as propriedades DataSource e
DataMember do DataGrid. Voc j deve estar visualizando o registro 1015 no
DataGrid, pois inicializamos esse valor na propriedade Value do BdpParameter
criando anteriormente.

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

Seu formulrio deve estar semelhante ao mostrado a seguir:

Tudo o que precisamos fazer agora repassar em tempo de execuo o


valor digitado no TextBox para o parmetro criado anteriormente, e reativar o
BdpDataAdapter. Isso feito no evento Click do boto:
procedure TWinForm2.Button1_Click(sender: System.Object; e: System.EventArgs);
begin
BdpDataAdapter1.Active := False;
BdpDataAdapter1.SelectCommand.Parameters[CUST_NO].Value := TextBox1.Text;
BdpDataAdapter1.Active := True;
end;

Parameters um objeto do tipo BdpParamterCollection, uma coleo de


BdpParameters. No cdigo anterior acessamos o parmetro CUST_NO atravs do seu nome, definido na criao do mesmo (propriedade ParameterName).
Execute a aplicao e digite um cdigo vlido no TextBox, e teste a consulta
parametrizada:

6. Transaes com o BdpTransaction


Transaes so importantes para garantir a integridade das atualizaes enviadas ao banco de dados. Por exemplo, se voc executar uma srie de updates,
e um deles falhar, talvez queira que toda a operao seja cancelada. Ou seja,
usando transaes, podemos garantir a atomicidade de uma srie de operaes
no BD. Costumo fazer a analogia com uma transao bancria. Um saque em
um caixa automtico requer uma srie de operaes. Se em algum momento
umas das operaes falhar, toda a transao deve ser desfeita (caso contrrio
um valor pode ser debitado de sua conta de forma incorreta).
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.22

Existem muitas aplicaes para o uso de transaes. Uma vantagem que o


BDP j gerencia automaticamente transaes quando usamos o
BdpDataAdapter. Algumas vezes, no entanto, voc pode querer controlar uma
transao manualmente. Veremos como fazer isso na prtica nesta sexta parte
do curso, usando o BdpTransaction.
No BDP, o BdpTransaction responsvel por gerenciar transaes com o
banco de dados. Use o mtodo BeginTransaction do BdpConnection para obter um objeto do tipo IDBTransaction. Os mtodos Commit e RollBack do
BdpTransaction so usados para efetivar ou no a transao. O nvel de isolamento pode ser passado como parmetro no BeginTransaction. Como voc
pode ver, h uma similaridade bastante grande com a nomenclatura usada no
DataSnap / dbExpress / BDE. A forma de utilizao tambm idntica, de
forma que voc no ter maiores dificuldades se j trabalha com acesso a dados no Delphi.
Criando a aplicao
Faremos agora um exemplo prtico que demonstra a utilizao do
BdpTransaction. Inicie uma nova aplicao do tipo Windows Forms Application.
Arraste para o designer a conexo Employee a partir do Data Explorer. Isso
cria um BdpConnection. Coloque um BdpCommand no formulrio e aponte
sua propriedade Connection para BdpConnection1.
Coloque agora no formulrio um Button, um Label e trs TextBoxes. Configure a propriedade MultiLine de um dos TextBoxes para True (para que ele
fique semelhante a um Memo da VCL). Esse TextBox exibir a mensagem de
erro gerada pelo BD.
Seu formulrio deve estar semelhante ao mostrado a seguir:

O exemplo funcionar da seguinte forma: digitaremos dois comandos SQL


nos TextBoxes, e executaremos ambos no contexto de uma nica transao.
Caso um deles falhe, damos um RollBack na transao e no efetivamos as
atualizaes, exibindo o erro no terceiro TextBox. Caso tudo funcione como
esperado, efetivamos a transao chamando um Commit.

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.23

Executando comandos com transao


No evento Click do boto digite o seguinte:
procedure TWinForm.btExec_Click(sender: System.Object; e: System.EventArgs);
var
Trans: IDbTransaction;
begin
BdpConnection1.Open;
try
Trans := BdpConnection1.BeginTransaction;
try
BdpCommand1.CommandText := TextBox1.Text;
BdpCommand1.ExecuteNonQuery;
BdpCommand1.CommandText := TextBox2.Text;
BdpCommand1.ExecuteNonQuery;
Trans.Commit;
Label1.Text := Commit - Transao encerrada com sucesso;
except
on E: Exception do
begin
Trans.Rollback;
Label1.Text := RollBack - Alteraes no efetivadas no BD;
TextBox3.Text := E.Message;
end;
end;
finally
BdpConnection1.Close;
end;
end;

Aqui usamos o mtodo BeginTransaction do BdpConnection para iniciar


uma transao.A seguir, usamos o componente BdpCommand para executar
os comandos digitados, atravs da sua propriedade CommandText. O mtodo
ExecuteNonQuery, como j vimos anteriormente neste curso, usado para
executar comandos que no retornam um conjunto de dados, como Update,
Delete ou Insert.
As operaes so protegidas por um bloco Try Except, que nos d a possibilidade de executar cdigo de tratamento caso um erro ocorra (nesse caso
damos um RollBack na transao). Um bloco Try Finally garante que, sob qualquer circunstncia, a conexo seja sempre fechada aps a operao.

Testando
Execute a aplicao e digite os comandos nos TextBoxes, como mostrado
na figura a seguir:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.24

O primeiro comando atualiza o campo CUSTOMER de todos os registros


de tabela CUSTOMER para o valor TESTE. O segundo comando exclui o
CUSTOMER que possui CUST_NO igual a 1015. Observe que o segundo
comando violar uma Foreign Key no servidor, j que existe uma referncia a
esse registro na tabela SALES. Nesse caso, a transao garantir que o primeiro comando no seja efetivado (a tabela no atualizada), desfazendo toda a
operao:

7. MetaData
Neste artigo veremos como utilizar o BDP para recuperar informaes sobre os Metadados de um banco de dados, usando basicamente o mtodo
GetMetaData de BdpConnection. Voc pode utilizar esse recurso para obter
dados sobre os objetos de um banco, como nome de tabelas, colunas, tipos,
tamanhos, ndices, views, triggers, procedures etc.
Cada banco de dados possui um catlogo, um conjunto de tabelas que armazenam informaes sobre o prprio banco de dados. Por exemplo, o DB2
guarda informaes sobre TABELAS e COLUNAS em tabelas chamadas
SYSTABLES e SYSCOLUMNS. O SQL Server chama de sysobjetcs e
syscolumns. O Oracle armazena essas informaes em tabelas como
USER_TABLES e USER_COLUMNS. O InterBase / Firebird armazena informaes em vrias tabelas de sistema, iniciando com o prefixo RDB$. Para
se obter informaes sobre Metadados, basta que voc d um Select na respectiva tabela, de acordo com o banco usado.
O problema que a mesma instruo no se aplica a outro BD, pois conforme vimos, as tabelas de catlogo possuem nomes e estruturas completamente
diferentes.
Sabendo isso, a Borland quando criou o BDP definiu uma interface (chamada ISQLMetaData) para padronizar a obteno dessas informaes, no importa o banco de dados que esteja utilizando. Ou seja, h um padro, e voc
no precisar escrever diferentes instrues SQL para obter informaes desse
tipo quando precisar acessar mais de um tipo de BD.
Dessa forma, cada driver do BDP (para DB2, Oracle, IB) deve implementar
essa interface para indicar como os metadados de um BD podem ser obtidos,
e isso fica transparente ao programador.
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.25

A prpria IDE do Delphi 8 utiliza os servios de metadados oferecidos pelo


BDP para exibir na janela Data Explorer informaes sobre objetos de um
banco de dados. Veja:

Como voc pode ver, o Data Explorer exibe em um TreeView informaes


sobre tabelas, campos, StoredProcedures (e parmetros), views etc.
O que faremos agora uma aplicao semelhante ao Data Explorer da IDE
(BDS), que permitir ao usurio explorar os objetos do banco de dados Employee
dos demos do Interbase.
Configurando os controles do formulrio
Inicie uma nova aplicao Windows Forms no Delphi 8. Coloque no formulrio um TreeView e dois DataGrids. Caso queria utilize Panels para servir de
container para os DataGrids, de forma que fiquem sempre a direita do formulrio. A propriedade Dock pode ser usada para alinh-los.
Coloque tambm os seguintes componentes: ImageList, BdpCommand,
BdpConnection, BdpDataAdapter e DataSet.
Abra o editor da propriedade Images do ImageList e adicione seis imagens,
clicando boto Add:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.26

Aponte a propriedade ImageListi do TreeView para ImageList1. Abra o


editor da propriedade Nodes do TreeView e adicione alguns nodes na rvore,
como mostrado a seguir:

.27
Nota: o componente DataSet
discutido em detalhes mais
adiante, ainda neste curso.
Por enquanto, basta saber que
ele responsvel pela cache
dados local de dados na aplicao cliente.

Utilize a opo Image para definir uma imagem para cada item.
Configurando o acesso a dados
A partir do Data Explorer da IDE arraste para o formulrio a conexo
Employee, para criar um BdpConnection. Coloque um BdpDataAdapter e aponte sua propriedade SelectCommand.Connection para BdpConnection1. Coloque um BdpCommand e aponte sua propriedade Connection para
BdpConnection1. Coloque um DataSet e aponte a propriedade DataSet do
BdpDataAdapter para esse componente. Seu formulrio deve estar semelhante
ao mostrado a seguir:

Respondendo aos eventos


No evento AfterSelect do TreeView digite o seguinte:
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

procedure TWinForm.TreeView1_AfterSelect(sender: System.Object;


e: System.Windows.Forms.TreeViewEventArgs);
var
dt: DataTable;
begin
case e.Node.ImageIndex of
1 : dt := BdpConnection1.GetMetaData.GetTables(,TableType.Table);
2 : dt := BdpConnection1.GetMetaData.GetTables(,TableType.View);
3 : dt := BdpConnection1.GetMetaData.GetProcedures(,
ProcedureType.Procedure);
end;
{ Selecionou um a Tabela }
if e.Node.ImageIndex = 4 then
begin
{ pega MetaData }
dt := BdpConnection1.GetMetaData.GetColumns(e.Node.Text,,ColumnType.Unknown);
DataGrid1.DataSource := dt;
{ pega dados }
BdpCommand1.CommandType := CommandType.TableDirect;
BdpCommand1.CommandText := e.Node.Text;
BdpDataAdapter1.Active := False;
BdpDataAdapter1.Active := True;
DataGrid2.DataSource := DataSet1.Tables[0];
DataGrid2.CaptionText := e.Node.Text + (Dados);
end;
DataGrid2.Visible := e.Node.ImageIndex = 4;
if dt <> nil then
begin
DataGrid1.DataSource := dt;
DataGrid1.CaptionText := e.Node.Text + (MetaDados);
end;
end;

O cdigo anterior verifica o tipo de n selecionado (tabela, view ou


StoredProcedure), e chama o mtodo GetMetaData do BdpConnection para
extrair os metadados do banco conforme a seleo do usurio.
GetMetaData possui vrios mtodos, por exemplo, GetTables retorna os
nomes das tabela do banco, enquanto GetProcedures retorna os nomes dos
StoredProcedures. GetColumns pode ser usado para obter as colunas (com
nome, tamanho, tipo etc.) de uma tabela do BD. O resultado da chamada desses mtodos um DataTable, componente interno ao DataSet, que armazena
uma estrutura de dados na aplicao cliente representando uma tabela do banco. Ou seja, voc pode utilizar o prprio DataGrid para exibir os dados
retornados pela chamada a esses mtodos, como fizemos no cdigo anterior.
Declare na seo strict private da classe do formulrio um mtodo chamado
ConfiguraTreeView, e implemente-o da seguinte forma:
procedure TWinForm.ConfiguraTreeView;
var
dt1, dt2: DataTable;
i,j,t: integer;
n1,n2: TreeNode;
s1,s2: string;
pNode: TreeNode;
row: DataRow;
begin
{ Preenche Tabelas e Views }
for t := 1 to 2 do
begin
if t = 1 then
// tabela
begin
pNode := TreeView1.Nodes[0].Nodes[0];
dt1 := BdpConnection1.GetMetaData.GetTables(,TableType.Table);

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.28

end
else // views
begin
pNode := TreeView1.Nodes[0].Nodes[1];
dt1 := BdpConnection1.GetMetaData.GetTables(,TableType.View);
end;
for i := 0 to pred(dt1.Rows.Count) do
begin
row := dt1.Rows[i];
s1 := row[TableName].ToString;
n1 := TreeNode.Create(s1);
n1.ImageIndex := 4;
n1.SelectedImageIndex := 4;
pNode.Nodes.Add(n1);
{ Preenche Campos para cada Tabela }
dt2 := BdpConnection1.GetMetaData.GetColumns(s1,,ColumnType.Unknown);
for j := 0 to pred(dt2.Rows.Count) do
begin
row := dt2.Rows[j];
s2 := row[ColumnName].ToString;
n2 := TreeNode.Create(s2);
n2.ImageIndex := 5;
n2.SelectedImageIndex := 5;
n1.Nodes.Add(n2);
end;
end;
end;
{ Preenche Procedures }
pNode := TreeView1.Nodes[0].Nodes[2];
dt1 := BdpConnection1.GetMetaData.GetProcedures(,ProcedureType.Procedure);
for i := 0 to pred(dt1.Rows.Count) do
begin
row := dt1.Rows[i];
s1 := row[ProcName].ToString;
n1 := TreeNode.Create(s1);
n1.ImageIndex := 3;
n1.SelectedImageIndex := 3;
pNode.Nodes.Add(n1);
dt2 := BdpConnection1.GetMetaData.GetProcedureParams(s1,);
for j := 0 to pred(dt2.Rows.Count) do
begin
row := dt2.Rows[j];
s2 := row[ParamName].ToString;
n2 := TreeNode.Create(s2);
n2.ImageIndex := 5;
n2.SelectedImageIndex := 5;
n1.Nodes.Add(n2);
end;
end;
end;

O cdigo anterior monta a estrutura do TreeView no lado esquerdo do formulrio, inicializando os valores dos ns, para exibir os nomes das tabelas,
StoredProcedures e views disponveis no BD.
No evento Load do formulrio chamamos esse mtodo e abrimos a conexo:
procedure TWinForm.TWinForm_Load(sender: System.Object; e: System.EventArgs);
begin
BdpConnection1.Open;
ConfiguraTreeView;
TreeView1.CollapseAll;
end;

Testando o Data Explorer


Execute a aplicao. Observe que o TreeView exibe agora os objetos do
BD. Expandindo uma tabela por exemplo, voc tem acesso aos seus campos.
Se expandir um StoredProcedure, voc pode ver seus parmetros:
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.29

.30

Clicando sobre uma tabela, os dois DataGrids so configurados. O primeiro


mostra os metadados recuperados para tabela, como nomes dos campos, tipos, tamanhos etc. O segundo mostra os dados da tabela:

O mesmo acontece para Views.


E finalmente, se voc clicar sobre um StoredProcedure, ver os metadados
desse objeto:

8. DataSets e DataTables
At agora conhecemos todos os recursos oferecidos pelos Managed
Providers, mais especificamente o provider da Borland (BDP). Esses componentes se encarregam de realizar operaes diretamente no BD, como conexo, execuo de comandos, transaes etc.
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

Neste artigo comearemos a estudar o segundo grupo de componentes da


arquitetura ADO.NET, que no dependem de uma conexo com o BD, pois
atuam sobre dados que esto na memria (cache) da aplicao cliente. O DataSet
e o DataTable so sem sombra de dvida os dois principais componentes da
arquitetura. De fato, a forma como o ADO.NET foi projetado, com base no
conceito de datasets desconectados, delega importantes tarefas e atribuies
para estes componentes.
O objeto DataSet (acessvel atravs do namespace System.Data) tem um
importante papel na arquitetura ADO.NET. Seu objetivo armazenar dados
fornecidos por um DataAdapter, fazendo uma cache local, em memria e
desconectada do banco de dados (por meio de objetos DataTable, visto a
seguir).
Um DataSet, juntamente com os objetos DataTable, DataColumn, DataRow,
DataView e DataRelation, faz parte do segundo grupo de componentes da arquitetura ADO.NET, os Content Components (chamados ainda de
desconectados). Voc pode utilizar um DataSet com qualquer um dos
Managed Providers fornecidos, como SQL e OLE DB, ou BDP no caso deste
curso.
Um DataSet no armazena dados vindos somente de um banco de dados
(trazidos por um DataAdapter), mas pode ser utilizado para ler dados de um
arquivo XML, para obter dados de um servidor de aplicao, de uma planilha,
ou ainda dados gerados em memria. Esse comportamento exatamente o
mesmo do ClientDataSet do DataSnap.
Uma das principais diferenas de um DataSet para um ClientDataSet do
DatSnap que um DataSet pode fazer cache de mais de uma tabela ao mesmo
tempo. Ele possui internamente uma coleo de objetos DataTable. Falando
em um nvel mais alto, imagine um DataSet como uma coleo de
ClientDataSets.
No ClientDataSet, por mais que sua instruo SQL possa trazer dados de
vrias tabelas usando um join, o que voc obtm so dados em formato tabular
que se parecem com uma nica tabela do banco de dados. J um DataSet do
ADO.NET se comporta como um mini banco de dados. Ou seja, ele capaz
de gerenciar vrias tabelas, relacionamentos, ndices e vises dentro de uma
mesma estrutura.
A figura abaixo mostra a estrutura de um DataSet:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.31

Para quem est acostumado a usar os DataSets da VCL, bom saber desde j que a maneira como o .NET Framework gerencia o cursor local de dados
completamente diferente. Em outras palavras, voc no navegar pelos registros de um DataSet conforme est acostumado a fazer em aplicaes de
BD na VCL. No .NET, classes especializadas (estudadas mais adiante neste
curso) se encarregam desse papel. O DataTable nada mais que uma matriz de
dados, onde as colunas so os campos e as linhas os registros. No j o conceito de registro atual.
Neste primeiro exemplo veremos como realizar operaes bsicas sobre
um DataTable, como fazer uma varredura examinando seus registros, excluir,
alterar, adicionar e localizar registros etc.
Observe que no utilizaremos ainda o recurso de DataBindings, de forma
que acessaremos os dados do DataTable diretamente (no brao). DataBindings
discutido mais adiante neste curso.
Configurando o acesso a dados e controles do formulrio
Inicie uma nova aplicao do tipo Windows Forms Application. Expanda a
conexo Employee no Data Explorer e arraste a tabela Country para o designer.
Isso cria um BdpConnection e um BdpDataAdapter. Coloque um DataSet e
aponte a propriedade DataSet do BdpDataAdapter para esse componente.
Ative o BdpDataAdapter.
Coloque no formulrio um ListView, sete Buttons, dois TextBoxes, trs Labels
e trs ComboBoxes. Ajuste-os conforme mostrado na figura a seguir:

Varrendo registros
No evento Click do boto Listar digite o seguinte:
var
Table: DataTable;
Row: DataRow;
Col: DataColumn;
Column: DataColumn;
i,j: integer;
LvItem: ListViewItem;
begin

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.32

ListView1.Clear;
Table := DataSet1.Tables[Country];
{ adiciona colunas do ListView conforme colunas do DataTable }
for i := 0 to pred(Table.Columns.Count) do
begin
Col := Table.Columns[i];
ListView1.Columns.Add(Col.ColumnName,100,HorizontalAlignment.Left);
end;
{ varra rows do DataTable }
for i := 0 to Table.Rows.Count - 1 do
begin
Row := Table.Rows[i];
lvItem := ListViewItem.Create;
ListView1.Items.add(LvItem);
for j := 0 to Table.Columns.Count - 1 do
begin
if j = 0 then
lvItem.Text := Row[j].ToString
else
LvItem.SubItems.Add(Row[j].ToString);
end;
end;
ComboBox1.Items.Clear;
for i := 0 to Table.Rows.Count - 1 do
ComboBox1.Items.Add(i.ToString);
end;

No cdigo anterior varremos os Rows (objetos DataRow) do DataTable


que armazena as informaes da tabela Country. Para cara Row percorremos
o array Columns (de objetos DataColumn), recuperando o valor de cada campo. Esses valores so exibidos no ListView. Observe que a navegao nos
registros do DataTable feito com um for, pois como comentei, o DataTable
semelhante a uma matriz.
O resultado do cdigo pode ser visto na figura a seguir:

Adicionando, Alterando e Removendo DataRows


O cdigo a seguir deve ser includo no manipulador do evento Click do
boto Adicionar. Ele se encarrega de criar um DataRow (chamando o mtodo
NewRow do DataTable), configurar os valores dos campos (conforme o usurio digitou nos TextBoxes) e inclu-lo no DataTable (mtodo Add).
procedure TWinForm.Button3_Click(sender: System.Object; e: System.EventArgs);
var
Table: DataTable;
Row: DataRow;
i: integer;

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.33

begin
Table := DataSet1.Tables[Country];
Row := Table.NewRow;
Row[0] := TextBox1.Text;
Row[1] := TextBox2.Text;
Table.Rows.Add(Row);
{ varre novamente }
Button1_Click(nil,nil);
end;

A figura abaixo mostra um registro (DataRow) adicionado no DataTable:

O cdigo a seguir deve ser includo no manipulador do evento Click do


boto Alterar. Ele se encarrega de alterar os valores dos campos do DataRow.
Escolha o ndice do registro a ser alterado no ComboBox:
procedure TWinForm.Button2_Click(sender: System.Object; e: System.EventArgs);
var
Table: DataTable;
Row: DataRow;
i: integer;
begin
Table := DataSet1.Tables[Country];
i := Convert.ToInt32(ComboBox1.Text);
Row := Table.Rows[i];
Row[0] := TextBox1.Text; // o mesmo que Row[Country]
Row[1] := TextBox2.Text; // o mesmo que Row[Currency]
Button1_Click(nil,nil);
end;

A figura a seguir mostra uma alterao no 4 DataRow:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.34

O cdigo a seguir deve ser includo no manipulador do evento Click do


boto Remover. Ele se encarrega de excluir um DataRow do DataTable (escolha no ComboBox o ndice do DataRow a ser excludo):
procedure TWinForm.Button4_Click(sender: System.Object; e: System.EventArgs);
var
Table: DataTable;
Row: DataRow;
i: integer;
begin
Table := DataSet1.Tables[Country];
i := Convert.ToInt32(ComboBox1.Text);
Table.Rows.RemoveAt(i);
{ varre novamente }
Button1_Click(nil,nil);
end;

Podemos tambm examinar o estado atual de um DataRow (se o mesmo


foi includo, alterado, excludo etc.). Isso feito no manipulador do evento Click
do boto RowState, que cria uma coluna no ListView exibindo o estado do
registro:
procedure TWinForm.Button5_Click(sender: System.Object; e: System.EventArgs);
var
Table: DataTable;
Row: DataRow;
i: integer;
lvItem: ListViewItem;
st: string;
begin
Button1_Click(nil,nil);
ListView1.Columns.Add(RowState,100,HorizontalAlignment.Left);
Table := DataSet1.Tables[0];
for i := 0 to pred(Table.Rows.Count) do
begin
Row := Table.Rows[i];
st := Enum.GetName(TypeOf(DataRowState),Row.RowState);
ListView1.Items[i].SubItems.Add(st);
end;
end;

A figura a seguir mostra o resultado da execuo do cdigo (observe a 3


coluna do ListView):

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.35

Procurando e selecionando
O cdigo inserido no evento Click do boto Find se encarrega de encontrar
um DataRow no DataTable de acordo com o valor digitado pelo usurio no
ComboBox1:
procedure TWinForm.Button6_Click(sender: System.Object; e: System.EventArgs);
var
Table: DataTable;
Row: DataRow;
pk: array [0..0] of DataColumn;
begin
Table := DataSet1.Tables[0];
pk[0] := Table.Columns[0];
Table.PrimaryKey := pk;
Row := Table.Rows.Find(ComboBox2.Text);
if Row <> nil then
MessageBox.Show(Encontrou)
else
MessageBox.Show(No encontrou)
end;

Observe que o campo a ser localizado deve ser a chave-primria do


DataTable, nesse caso estamos usando o campo Country, definido na propriedade PrimaryKey. A seguir, usamos o mtodo Find da coleo Rows do
DataTable para localizar um registro. Observe que Find retorna apenas um
registro.
A figura abaixo mostra uma procura pelo Country USA:

O cdigo a seguir, inserido no evento Click do boto Select, se encarrega


de encontrar DataRows no DataTable de acordo com a condio de busca
especificada:
procedure TWinForm.Button7_Click(sender: System.Object; e: System.EventArgs);
var
Table: DataTable;
Rows: array of DataRow;
i: integer;
st: string;
begin
Table := DataSet1.Tables[0];
Rows := Table.Select(ComboBox3.Text);
for i := low(Rows) to high(Rows) do
st := st + Rows[i][0].ToString + #13;
MessageBox.Show(st);
end;

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.36

A figura abaixo mostra uma procura por todos os DataRows cujo campo
Country comece com A:

Observe que o comando LIKE (usando em consultas SQL) aqui usado


localmente, sob os dados que j esto na memria da aplicao. No h consulta extra ao BD.

9. DataRelations
Neste artigo veremos como trabalhar com relacionamentos, usando a propriedade Relations do componente DataSet. Como j comentamos em artigos
anteriores, o DataSet permite que voc armazene em uma mesma estrutura
informaes retornadas de mltiplas tabelas do BD, em objetos DataTable. A
seguir, voc pode especificar como esses dados se relacionam, configurando
inclusive regras de integridade, que sero verificadas na aplicao cliente.
Configurando os BdpDataAdapters
Inicie uma nova aplicao do tipo Windows Forms Application. Expanda a
conexo Employee no Data Explorer e arraste a tabela Department para o
designer. Isso cria um BdpConnection e um BdpDataAdapter. Arraste tambm
a tabela Employee, para criar um segundo BdpDataAdapter.
Veja como ficou a instruo SQL do primeiro BdpDataAdapter, que recupera informaes da tabela DEPARMENT (para ver a instruo configurada,
clique de direita sobre o componente e selecione Configure Data Adapter, ou
abra o editor da propriedade SelectCommand.CommandText):
SELECT
DEPT_NO,
DEPARTMENT,
HEAD_DEPT,
MNGR_NO,
BUDGET,
LOCATION,
PHONE_NO
FROM
DEPARTMENT

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.37

E aqui est a instruo do BdpDataAdapter2, que recupera informaes


sobre a tabela EMPLOYEE:
SELECT
EMP_NO,
FIRST_NAME,
LAST_NAME,
PHONE_EXT,
HIRE_DATE,
DEPT_NO,
JOB_CODE,
JOB_GRADE,
JOB_COUNTRY,
SALARY,
FULL_NAME
FROM
EMPLOYEE

Gerando o DataSet
Clique de direita sobre o BdpDataAdapter1 e selecione a opo Configure
Data Adapter. Na aba DataSet selecione a opo New DataSet:

Clique em Ok. Isso dever gerar um DataSet no formulrio:

Clique de direita sobre o BdpDataAdapter2 e selecione a opo Configure Data


Adapter. Na aba DataSet selecione a opo Existing DataSet:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.38

Clique em Ok. Observe que geramos ambas as consultas em um mesmo


DataSet. Defina como True a propriedade Active de ambos os
BdpDataAdapters. Observe ainda que a propriedade DataSet dos dois
BdpDataAdapters aponta para DataSet1.
Abra a propriedade Tables do DataSet e voc dever ver os dois DataTables
gerados:

Criando o relacionamento
Abra o editor da propriedade Relations do DataSet e no editor clique no
boto Add:

Na janela que aparece d um nome para o relacionamento. Em Parent Table


defina qual a tabela mestre e em Child table escolha a tabela detalhe. Em Columns
relacione os campos DEPT_NO das duas tabelas, conforme mostrado a seguir:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.39

Com isso, configuramos uma relao mestre-detalhe entre os dois


DataTables, semelhante ao que estvamos acostumados a fazer usando DataSets
da VCL (lembra do MasterSource e MasterField?).
A grande novidade que podemos configurar regras de integridade para os
dados do DataSet, semelhante ao que se consegue em bancos relacionais usando Foreign Keys. A diferena, claro, que tudo ser feito na aplicao cliente.
Por exemplo, configure o Update rule para Cascade. Com isso, quando o
campo DEPT_NO da tabela DEPARTMENT for atualizado para 1000, por
exemplo, todos os registros relacionados na tabela EMPLOYEE tero o valor
do campo DEPT_NO atualizados para 1000, garantindo a integridade dos dados.
Delete rule indica o que deve ser feito quando um registro na tabela mestre
for excludo. Cacade indica que todos os registros relacionados sero apagados. Clique no boto Help para obter mais informaes sobre cada um dos
tipos disponveis nessas opes.
Configurando o DataGrid
Coloque no formulrio um DataGrid. Configure sua propriedade DataSource
para DataSet1. Observe que em DataMember so listados todos os DataTables
disponveis:

Escolha DEPARTMENT e rode a aplicao. Observe que o DataGrid exibe um sinal + para que voc possa recuperar os registros do DataTable relacionado:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.40

Por exemplo, selecione o DEPTARMENT 100, clique em + e a seguir no


link que aparece mostrando o nome do relacionamento. Com isso vamos obter
todos empregados (EMPLOYEE) do departamento selecionado:

Voc tambm pode querer exibir os dados da tabela mestre e da tabela


detalhe ao mesmo tempo na tela. Para isso, coloque um segundo DataGrid e
aponte sua propriedade DataSource para DataSet1. Agora o segredo no
apontar o DataMember para o DataTable EMPLOYEE, mas sim para o prprio relacionamento:

Execute a aplicao e veja o resultado:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.41

10. DataSet e XML


O ADO.NET utiliza o formato XML para intercmbio de dados. Dessa
forma, voc pode facilmente distribuir DataSets atravs da Web e utilizar recursos de interoperabilidade, j que XML independente de plataforma.
No .NET Framework, as classes base para manipulao de documentos
XML esto localizadas no namespace System.XML (como XMLNode,
XMLElement, XMLDocument etc). A utilizao dessas classes semelhante
ao uso do componente XMLDocument no Delphi. Alm dessas classes, um
DataSet do ADO.NET (assim como um ClientDataSet do Delphi) pode ser
totalmente representado em formato XML. Voc pode simplesmente chamar
seu mtodo WriteXML e passar como parmetro o nome do arquivo (ou ainda
uma string ou uma Stream).
Por padro, WriteXML exporta somente os dados de um DataSet. Voc
pode passar com segundo parmetro de WriteXML a opo
XMLWriteMode.WriteSchema para salvar a definio de campos, constraints
e relacionamentos usando uma XML Schema (XSD). Para recuperar o contedo de um DataSet voc utilizar o mtodo ReadXML.
Neste artigo veremos como salvar os dados de um DataSet em um arquivo
XML, e como recuperar esse dados.
Configurando os componentes do BDP
Inicie uma nova aplicao do tipo Windows Forms Application. Expanda a
conexo Employee no Data Explorer e arraste a tabela Department para o
designer. Isso cria um BdpConnection e um BdpDataAdapter. Coloque um
DataSet e aponte a propriedade DataSet do BdpDataAdapter para esse componente, e configure seu Active para True.
Coloque tambm os seguintes componentes: um OpenFileDialog, quatro
Buttons, um Label, um CheckBox, um TextBox e um DataGrid. Seu formulrio
deve estar semelhante ao mostrado a seguir:

WriteXml e ReadXml
No evento Click do boto Abrir abrimos a consulta configurada no
BdpDataAdapter, e configuramos o DataGrid:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.42

procedure TWinForm.Button3_Click(sender: System.Object; e: System.EventArgs);


begin
BdpDataAdapter1.Active := True;
DataGrid1.DataSource := DataTable1;
end;

.43

No evento Click do boto WriteXML chamamos o mtodo WriteXML do


DataSet, que gravar os dados do BD em formato XML. Se o usurio marcou
o Checkbox, gravamos o XML com schema, passando o parmetro
XmlWriteMode.WriteSchema para o mtodo:
procedure TWinForm.Button1_Click(sender: System.Object; e: System.EventArgs);
begin
if CheckBox1.Checked then
dataSet1.WriteXml(TextBox1.Text,XmlWriteMode.WriteSchema)
else
dataSet1.WriteXml(TextBox1.Text,XmlWriteMode.IgnoreSchema);
end;

No boto ReadXML carregamos o arquivo XML, que pode ou no usar


schema:
procedure TWinForm.Button2_Click(sender: System.Object; e: System.EventArgs);
begin
dataSet1.Clear;
if CheckBox1.Checked then
dataSet1.ReadXml(TextBox1.Text,XmlReadMode.ReadSchema)
else
dataSet1.ReadXml(TextBox1.Text,XmlReadMode.IgnoreSchema);
DataGrid1.DataSource := DataTable1;
end;

No evento Click do boto ao lado do textBox abrimos a caixa de dilogo


que permite escolher um arquivo a ser carregado:
procedure TWinForm.Button5_Click(sender: System.Object; e: System.EventArgs);
begin
OpenFileDialog1.ShowDialog;
TextBox1.Text := OpenFileDialog1.FileName;
end;

Testando
Execute a aplicao e clique no boto Abrir:

A seguir escolha um arquivo clicando no boto ao lado do textBox e clique


em WriteXml. Observe que os dados foram salvos para um arquivo XML:
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

Nota: o mtodo WriteXML


do DataSet seria semelhante
ao
SaveToFile
do
ClientDataSet do DataSnap,
enquanto ReadXML seria semelhante ao LoadFromFile.

.44

Resete a aplicao, e ao invs de clicar no boto Abrir (o que traria os


dados do BD para o DataSet), carregue o arquivo XML clicando em ReadXml:
Com isso voc pode simular uma aplicao MyBase, que feito no Delphi
usando ClientDataSet. Ou seja, voc pode construir aplicaes desktop (1
camada) sem conexes a um BD SQL, salvando e lendo dados de arquivos
XML locais.

11. DataBindings
Nesta parte do curso conheceremos o poderoso mecanismo de DataBindings
do .NET Framework, que permite exibir dados em controles de tela.
Onde esto os Data Controls no .NET?
Na VCL do Delphi, sempre que precisamos exibir dados de um BD fazemos uso dos famosos controles Data-Aware. Ou seja, se voc quer exibir dados de um DataSet em um Edit, por exemplo, deve usar sua verso DataAware, o DBEdit.
Isso exige que cada componente que precise exibir informaes de um BD
precise ter uma verso Data-Aware, o que conseguido implementando-se
DataLinks internos.
No .NET Framework, possvel vincular um controle de tela a uma fonte de
dados usando o recurso de DataBindings. Isso difere um pouco da VCL, pelo
fato que voc usar os mesmos controles no Data-Aware do Windows Forms
tambm em uma aplicao de BD. Basta configurar sua propriedade DataBingings
para que o controle passe a reconhecer dados. Uma outra vantagem que esse
comportamento definido na classe Control do Windows Forms, ou seja, todos os controles de tela (Button, TextBox, Label etc.) podem fazer Binding de
dados, no necessrio criar ou usar classes especficas.
Configurando o formulrio e acesso a dados
Inicie uma nova aplicao do tipo Windows Forms Application. Expanda a
conexo Employee no Data Explorer e arraste a tabela Employee para o designer.
Isso cria um BdpConnection e um BdpDataAdapter. Coloque um DataSet e
aponte a propriedade DataSet do BdpDataAdapter para esse componente.
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

Coloque agora no formulrio os seguintes componentes, conforme mostrado no formulrio a seguir (os componentes utilizados so: DataGrid, Buttoi,
TextBox, TabControl - com trs TabPages, ProgressBar, Trackbar, RadioButton,
Label, CheckBox, GroupBox, LixtBox e StatusBar):

DataBinding
Suponha agora que voc queira exibir no TextBox o valor para o campo
FIRST_NAME do DataTable EMPLOYEE. Para isso, basta apontar a propriedade DataBindings.Text como mostrado a seguir:

Tambm podemos criar o relacionamento em tempo de execuo, como fiz


aqui no evento Click do boto Testar DataBinding:
procedure TWinForm.Button2_Click(sender: System.Object; e: System.EventArgs);
const
StrArray: array [0..4] of string =
(Guinther,Rudolfo,Corbin,Gladstone,Pimenta);
begin
DataBindings.Add(
binding.Create(Text,DataSet1.Tables[0],FULL_NAME));
Button3.DataBindings.Add(
binding.Create(Text,DataSet1.Tables[0],FULL_NAME));
TextBox1.DataBindings.Add(
binding.Create(Text,DataSet1.Tables[0],FULL_NAME));
StatusBar1.DataBindings.Add(
binding.Create(Text,DataSet1.Tables[0],FULL_NAME));
Label1.DataBindings.Add(
binding.Create(Text,DataSet1.Tables[0],FULL_NAME));

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.45

RadioButton1.DataBindings.Add(
binding.Create(Text,DataSet1.Tables[0],FULL_NAME));
CheckBox1.DataBindings.Add(
binding.Create(Text,DataSet1.Tables[0],FULL_NAME));
TabPage1.DataBindings.Add(
binding.Create(Text,DataSet1.Tables[0],FULL_NAME));
GroupBox1.DataBindings.Add(
binding.Create(Text,DataSet1.Tables[0],FULL_NAME));
DataGrid1.DataBindings.Add(
binding.Create(CaptionText,DataSet1.Tables[0],FULL_NAME));
ProgressBar1.DataBindings.Add(
binding.Create(Value,DataSet1.Tables[0],EMP_NO));
TrackBar1.DataBindings.Add(
binding.Create(Value,DataSet1.Tables[0],EMP_NO));
{ data binding de um array para um ListBox }
ListBox1.DataSource := StrArray;
end;

Aqui chamamos o mtodo Add da propriedade DataBindings de cada controle, passando como parmetro o nome da propriedade onde o valor deve ser
mostrado e de qual datasource deve ser obtido.
Observe aqui uma grande vantagem dessa abordagem: voc pode fazer o
Binding de um valor para mais de uma propriedade de um mesmo componente.
Por exemplo, observe que fiz o Binding do valor do campo EMP_NO do
DataTable para a propriedade Value do TrackBar. Na VCL, geralmente o valor exibido no texto do controle. Aqui no temos essa limitao.
Os botes < e > fazem a navegao entre os registros, para que voc
possa ver os valores mudando nos controles que fizemos o DataBinding:
procedure TWinForm.Button5_Click(sender: System.Object; e: System.EventArgs);
begin
with BindingContext.Item[DataSet1.Tables[0],] do
Position := Position + 1;
end;
procedure TWinForm.Button4_Click(sender: System.Object; e: System.EventArgs);
begin
with BindingContext.Item[DataSet1.Tables[0],] do
Position := Position - 1;
end;

Observe o DataBinding em ao na tela abaixo, onde exibimos valores do


DataTable nos mais variados tipos de componentes:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.46

Observe ainda uma outra vantagem do DataBinding: os dados no precisam


necessariamente virem de um DataSet / DataTable. Veja que no ListBox estou
exibindo alguns nomes, que no vieram do DataTable em questo. Isso foi conseguido fazendo o Binding do controle para um Array (isso mesmo!), conforme
mostrado no cdigo a seguir:
const
StrArray: array [0..4] of string =
(Guinther,Rudolfo,Corbin,Gladstone,Pimenta);
begin
...
ListBox1.DataSource := StrArray;

12. CurrencyManager
O CurrencyManager usado para gerenciar uma lista de objetos que usam
DataBinding. Ele pode controlar o posicionamento de um DataView (lembrese que um DataTable no possui o conceito de registro atual), permitindo que
controles em tela podem ser sincronizados para exibirem sempre os dados de
um mesmo registro. Para obter um CurrencyManager, use o BindContext (uma
coleo de BindingManagerBase, que classe base de CurrencyManager)
passando como parmetro o nome do DataSource.
Neste artigo veremos operaes tpicas com o CurrencyManager, como
navegao e manipulao de dados.
Configurando os componentes do BDP
Inicie uma nova aplicao do tipo Windows Forms Application. Expanda a
conexo Employee no Data Explorer e arraste a tabela Department para o
designer. Isso cria um BdpConnection e um BdpDataAdapter. Coloque um
DataSet e aponte a propriedade DataSet do BdpDataAdapter para esse componente, e configure seu Active para True.
Coloque no formulrio trs Labels, trs TextBoxes, dez Buttons, um
ErrorProvider e um StatusBar. Ajuste os componentes no formulrio conforme
mostrado na figura a seguir:

Configure a propriedade DataBindings dos TextBoxes para apontarem para


os campos EMP_NO, FIRST_NAME e LAST_NAME do DataTable1, respectivamente.
Usando o CurrencyManager
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.47

Declare o seguinte na seo private do formulrio:


private
CM: CurrencyManager;
procedure CMPositionChanged(Sender: &object; E: EventArgs);

Implemente o mtodo anterior da seguinte forma:


procedure TWinForm.CMPositionChanged(Sender: &object; E: EventArgs);
var
n: integer;
begin
btFirst.Enabled := CM.Position > 0;
btPrior.Enabled := CM.Position > 0;
btNext.Enabled := CM.Position < CM.Count - 1;
btLast.Enabled := CM.Position < CM.Count - 1;
n := CM.Count - 1;
StatusBar1.Text := CM.Position.ToString + de + n.ToString;
end;

Esse mtodo ser disparado quando a posio de um registro em tela for


modificada. De acordo com a posio, configuramos quais botes de navegao permanecero habilitados. Para isso, usamos a propriedade Position de
CM (CurrencyManager). A varivel CM inicializada no evento Load do formulrio:
procedure TWinForm.TWinForm_Load(sender: System.Object; e: System.EventArgs);
begin
// obtm o CurrencManager para o DataView DataTable1
CM := Self.BindingContext.Item[dataSet1.Tables[EMPLOYEE]] as CurrencyManager;
Include(CM.PositionChanged,CMPositionChanged);
CMPositionChanged(nil,nil);
end;

Veja a seguir o cdigo que deve ser colocado para cada um dos botes da
barra de navegao, na ordem. O que fazemos basicamente chamar os mtodos do CM (CurrencyManager):
procedure TWinForm.btFirst_Click(sender: System.Object; e: System.EventArgs);
begin
CM.Position := 0;
end;
procedure TWinForm.btPrior_Click(sender: System.Object; e: System.EventArgs);
begin
CM.Position := CM.Position - 1;
end;
procedure TWinForm.btNext_Click(sender: System.Object; e: System.EventArgs);
begin
CM.Position := CM.Position + 1;
end;
procedure TWinForm.btLast_Click(sender: System.Object; e: System.EventArgs);
begin
CM.Position := CM.Count - 1;
end;
procedure TWinForm.btCancel_Click(sender: System.Object; e: System.EventArgs);
begin
CM.CancelCurrentEdit;
end;
procedure TWinForm.btPost_Click(sender: System.Object; e: System.EventArgs);
begin
CM.EndCurrentEdit;
end;
procedure TWinForm.btDelete_Click(sender: System.Object; e: System.EventArgs);
begin
CM.RemoveAt(CM.Position);
end;
procedure TWinForm.AddNew(sender: System.Object; e: System.EventArgs);
begin
CM.AddNew;
end;
procedure TWinForm.btRefresh_Click(sender: System.Object; e: System.EventArgs);
begin

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.48

CM.Refresh;
end;
procedure TWinForm.Update(sender: System.Object; e: System.EventArgs);
begin
BdpDataAdapter1.AutoUpdate;
end;

.49

Execute a aplicao e veja o resultado na figura a seguir:

Nota: A partir desta parte do


curso utilizaremos o Delphi
2005, no entanto sinta-se a
vontade para usar o Delphi 8,
caso queira.
Validaes
Um recurso bastante interessante que vou mostrar aqui, no relacionando
diretamente ao Currencymanager (mas que merece destaque), o uso do componente ErrorProvider do .NET. Para entender como ele funciona, nada melhor do que conhece-lo em uma aplicao prtica.
Selecione o TextBox que representa o campo FIRST_NAME e no seu evento Validating digite o seguinte:
procedure TWinForm.TextBox2_Validating(sender: System.Object; e: System.ComponentModel.CancelEventArgs);
begin
if tbFirstName.Text = then
begin
ErrorProvider1.SetError(tbFirstName,First Name no pode ficar em branco!);
e.Cancel := True;
end
else
ErrorProvider1.SetError(tbFirstName,);
end;

Isso valida a entrada no campo, de forma que ele deve ser sempre preenchido (obrigatrio). Execute a aplicao e insira um registro, mantendo esse campo em branco. Veja o resultado:

13. Ordenando, Filtrando e Procurando


Dando continuidade ao nosso curso sobre acesso a dados no Delphi for
.NET com ADO.NET e BDP, veremos neste artigo como ordenar, filtrar e
procurar registros em DataTables de um DataSet.
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

Configurando os componentes do BDP


Inicie uma nova aplicao do tipo Windows Forms Application. Expanda a
conexo Employee no Data Explorer e arraste a tabela Department para o
designer. Isso cria um BdpConnection e um BdpDataAdapter. Arraste tambm
a tabela Employee para criar um segundo BdpDataAdapter. Coloque um
DataSet, aponte a propriedade DataSet do BdpDataAdapter1 para DataSet1
e configure seu Active para True. Faa o mesmo no BdpDataAdapter2.

Configure a propriedade Relations do DataSet conforme mostrado na figura


a seguir:
Configurando a interface de usurio
Coloque no formulrio cinco CheckBoxes, quatro DropDownListBoxes,
dois Buttons e um DataGrid (aponte o DataGrid para o DataTable1). Ajuste os
controles no formulrio como mostrado a seguir:

Ordenando
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.50

Veremos agora como ordenar os dados que esto no DataTable do DataSet


por uma coluna, que ser selecionada em um DropDownListBox. Observe que
essa ordenao feita em memria, no ser necessrio obter os dados novamente a partir do servidor SQL, o que ocasionaria um trfego desnecessrio de
dados na rede.
No evento SelectedIndexChanged do primeiro DropDownListBox digite o
seguinte:
procedure TWinForm1.cbSort_SelectedIndexChanged(sender: System.Object; e: System.EventArgs);
begin
if not cbDesc.Checked then
DataTable1.DefaultView.Sort := cbSort.Text
else
DataTable1.DefaultView.Sort := cbSort.Text + DESC;
end;

Aqui estamos acessando o DefaultView do primeiro DataTable. Veremos


mais sobre DataViews na prxima parte do curso, por ora basta saber que
cada DataTable possui um DefaultView que permite manipular, ordenar, filtrar e
buscar dados. Para ordenar os dados, basta atribuir para a propriedade Sort o
nome do campo desejado. Se colocarmos DESC ao final da propriedade, os
dados sero ordenados em ordem decrescente fazemos isso se o usurio
marcou o respectivo CheckBox.
Para preencher o DropDownListBox com os nomes dos campos disponveis, vamos varrer a coleo Columns do DataTable, que representa as colunas
da tabela. No evento Load do formulrio escreva o seguinte:
procedure TWinForm1.TWinForm1_Load(sender: System.Object; e: System.EventArgs);
var
i: integer;
begin
for i := 0 to DataTable1.Columns.Count - 1 do
cbSort.Items.Add(DataTable1.Columns[i].ToString);
end;

Caso queira, voc pode utilizar o novo comando for in do Delphi 2005
(anlogo ao foreach do C#), para iterar pelos itens da coleo, ao invs de usar
um for indexado.
Veja o resultado na figura abaixo, onde ordenamos a tabela pelo campo
Department. Observe que o DataGrid coloca uma pequena seta no cabealho
da coluna para indicar a ordenao.

Filtrando
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.51

Para filtrar um DataTable, basta atribuir uma string de filtro (ex. DEPT_NO
> 100) para a propriedade RowFilter do seu DefaultView (semelhante a propriedade Filter dos DataSets da VCL, exceto que voc no precisa configurar
algo como o Filtered para True).
Digite o seguinte no evento Click do boto Filtrar:
procedure TWinForm1.Button1_Click(sender: System.Object; e: System.EventArgs);
begin
DataTable1.DefaultView.RowFilter := cbFilter.Text;
end;

Veja um exemplo de filtragem na figura abaixo, onde selecionamos todos os


departamentos entre 110 e 120:

Veja ainda mais alguns exemplos de filtragem que voc pode utilizar:
DEPARTMENT LIKE S%

Estilo like do SQL; seleciona todos os departamentos onde o campo


Department comece com S.
LOCATION IN (Monterey,San Francisco)

Seleciona todos os departamentos onde o campo Location seja igual a


Monterey ou San Francisco.
Substring(DEPARTMENT,2,1) = u

Substring uma funo especial que pode ser usada em expresses de filtro.
Aqui selecionamos todos os departamentos onde o campo Department tenha a
segunda letra igual a u. Isso retorna, neste exemplo, Quality Assurance,
Customer Support, Customer Services e European Headquarters.
IIF(DEPT_NO<600,LOCATION=San

Francisco,LOCATION=Monterey)

IIF outra funo especial que pode ser usada em filtros. Passamos para ele
trs parmetros: O primeiro uma expresso booleana, se for verdadeira o
ADO.NET considera o segundo parmetro como sendo o filtro a ser usado,
caso contrrio, considera o terceiro. No exemplo anterior, seriam selecionados
todos os departamentos como localizao em San Francisco e DEPT_NO menor que 600 e todos os maiores que 600 que se localizam em Monterey. O
resultado pode ser visto na figura a seguir:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.52

.53
Sem IIF seria necessrio o uso de quatro expresses, com dois operadores
and e dois operadores or. Veja como simples fazer o mesmo com IIF no
exemplo anterior.
Count(Child(Relation1).EMP_NO)>2

Como temos um Relation configurado entre os dois DataTables, podemos


utilizar mais algumas funes especiais, como Child. No exemplo anterior, selecionamos todos os departamentos onde a tabela relacionada tenha pelo menos
2 registros, ou seja, s mostramos na tela os departamentos com mais de 2
funcionrios.
Localizando
Para localizar um registro, usamos o mtodo Find do DefaultView do
DataTable e passamos o valor a ser localizado, com base no campo chave. Isso
feito no evento Click do boto Find:
procedure TWinForm1.Button2_Click(sender: System.Object; e: System.EventArgs);
begin
DataTable1.DefaultView.Find(cbFind.Text);
end;

Isso faz papel semelhante ao Locate, FindKey, FindNearest, GotoKey e


GotoNearest dos DataSets da VCL. Veja porm que um DataTable no possui o recurso de registro atual (ele na verdade uma matriz), quem se encarrega
de gerenciar o posicionamento em tela e sincronizao de controles com
DataBinding o CurrencyManager (e DataView), como vimos em artigos anteriores.
Restringindo a Edio, Alterao e Excluso
A propriedade DefaultView ainda permite que sejam especificadas quais as
operaes permitidas sobre o DataTable utilizado. Em nosso exemplo, colocamos trs CheckBoxes (AllowDelete, AllowEdit e AllowNew) para configurar
essas opes. Aqui esto os manipuladores para os eventos CheckedChanged
de cada CheckBox:
procedure TWinForm1.cbAllowNew_CheckedChanged(sender: System.Object; e: System.EventArgs);
begin
DataTable1.DefaultView.AllowNew := cbAllowNew.Checked;
end;
procedure TWinForm1.cbAllowEdit_CheckedChanged(sender: System.Object; e: System.EventArgs);
begin
DataTable1.DefaultView.AllowEdit := cbAllowEdit.Checked;
end;
procedure TWinForm1.cbAllowDelete_CheckedChanged(sender: System.Object; e: System.EventArgs);

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

begin
DataTable1.DefaultView.AllowDelete := cbAllowDelete.Checked;
end;

Com isso os dados s porem ser alterados, excludos ou inseridos caso


essas opes estejam ativadas. Isso ideal para criar cadastro de usurios e
permisses, restringindo o acesso a determinadas operaes caso o usurio
no tenha permisso.
Filtrando pelo status do registro
E finalmente, o DefaultView permite que sejam exibidos na tela somente
registros que estejam em um determinado estado. Por exemplo, podemos exibir em um determinado momento somente registros que foram includos, ou
editados. Isso feito atravs da sua propriedade RowStateFilter (semelhante
ao que o StatusFilter do ClientDataSet da VCL).
Isso feito no evento SelectedIndexChanged do ltimo DropDownListBox:
procedure TWinForm1.cbRowStateFilter_SelectedIndexChanged(sender: System.Object;
e: System.EventArgs);
begin
DataTable1.DefaultView.RowStateFilter :=
DataViewRowState(Enum.Parse(TypeOf(DataViewRowState),cbRowStateFilter.Text));
end;

Aqui usamos reflexo do .NET (algo com o RTTI do Delphi) e o objeto


Enum para converter para um valor de tipo enumerado o valor string escolhido
no DropDownListBox. Isso nos poupa de fazer vrios ifs. Os itens do
DropDownListBox so preenchidos no evento Load do form:
Self.cbRowStateFilter.Items.AddRange(TArrayOfSystem_Object.Create(Added ,
CurrentRows , Deleted , ModifiedCurrent , ModifiedOriginal,
ModifiedOriginal , None , OriginalRows , Unchanged ));

A figura abaixo mostra o funcionamento do RowStateFilter, exibindo somente os registros que foram excludos at o momento:

14. DataViews
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.54

Dando continuidade ao nosso curso sobre acesso a dados no Delphi for


.NET com ADO.NET e BDP, veremos neste artigo como utilizar o componente DataView do ADO.NET.
Um DataView permite que voc crie diferentes vises de um mesmo conjunto de dados (DataTable). Voc pode ter diferentes controles na tela exibindo
os dados de um mesmo conjunto, porm usando uma ordenao ou filtro diferente em cada um. Na VCL, se voc filtrar um ClientDataSet, essa alterao
ser repercutida em todos os controles de tela associados ao DataSource/
DataSet. A vantagem no ADO.NET com DataView justamente essa: oferecer
diferentes maneiras de visualizao de um mesmo DataTable.
Neste artigo, faremos um exemplo simples que mostrar como exibir dados
de um DataTable em dois DataGrids, cada um usando um filtro diferente de
dados. Voc j deve estar familiarizado com o conceito de DataViews, pois
fizemos vrios exemplos na parte anterior deste curso usando o DefaultView do
DataTable.
Configurando os componentes do BDP e ADO.NET
Inicie uma nova aplicao do tipo Windows Forms Application. Expanda a
conexo Employee no Data Explorer e arraste a tabela Department para o
designer. Isso cria um BdpConnection e um BdpDataAdapter. Coloque um
DataSet e aponte a propriedade DataSet do BdpDataAdapter para esse componente e configure seu Active para True.
Coloque dois componentes DataView no formulrio e aponte a propriedade
Table de ambos para DataTable1. Para DataView configure o RowFilter como
EMP_NO < 10 e para o segundo EMP_NO > 140. Com isso temos dois
DataViews diferentes, cada um com um filtro especfico, atuando sobre o mesmo DataTable.
Configurando a interface de usurio
Coloque no formulrio dois DataGrids, apontando o DataSource do primeiro para DataView1 e do segundo para DataView2. Seu formulrio deve estar
semelhante ao mostrado a seguir:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.55
Nota: A partir desta parte do
curso utilizaremos o Delphi
2005, no entanto sinta-se a
vontade para usar o Delphi 8,
caso queira.

Execute e teste a aplicao. Observe que cada DataGrid exibe seus respectivos dados e o filtro no interfere no outro DataGrid. As alteraes e demais
operaes de manipulao de dados (incluso, excluso etc.) so refletidas no
mesmo DataTable. Voc pode utilizar tambm no DataView os recurso que
mostramos no artigo anterior com o DefaultView do DataTable, como localizao (Find), restrio de acesso (AllowDelete, AllowNew e AllowEdit) etc.

.56

15. Expressions
Dando continuidade ao nosso curso sobre acesso a dados no Delphi for
.NET com ADO.NET e BDP, veremos neste artigo como utilizar o recurso de
Expressions do ADO.NET.
Um Expression permite que voc crie um campo para um DataTable que
pode exibir um resultado de operaes em outros campos do DataTable, como
somas, contagens, clculos, mdias etc. Esse recurso semelhante aos
Aggregates do ClientDataSet da VCL.
Configurando os componentes do BDP e ADO.NET
Inicie uma nova aplicao do tipo Windows Forms Application. Expanda a
conexo Employee no Data Explorer e arraste as tabelas Department e Employee
para o designer. Isso cria um BdpConnection e dois BdpDataAdapters. Coloque um DataSet e aponte a propriedade DataSet do BdpDataAdapter para
esse componente e configure seu Active para True. Faa o mesmo para o segundo BdpDataAdapter.
Use a propriedade Relations do DataSet para configurar um relao entre
os dois DataTables, conforme mostrado a seguir:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

Nota: A partir desta parte do


curso utilizaremos o Delphi
2005, no entanto sinta-se a
vontade para usar o Delphi 8,
caso queira.

J discutimos Relations em partes anteriores deste curso, de forma que no


entrarei em detalhes aqui.
Configurando a interface de usurio
Coloque no formulrio u DataGrid e configure-o para apontar para o
DataTable1 do DataSet1. Coloque no formulrio seis TextBoxes, cinco Labels
e um Button.
Configure os componentes como mostrado a seguir:

No evento Load do formulrio escreva o seguinte:


procedure TWinForm.TWinForm_Load(sender: System.Object; e: System.EventArgs);
var
dgcol: DataGridTextBoxColumn;
begin
{ adiciona colunas com Expressions }
DataTable1.Columns.Add(Count,
TypeOf(Integer),COUNT(DEPT_NO));
DataTable1.Columns.Add(MinSalary,
TypeOf(Double),MIN(SALARY));
DataTable1.Columns.Add(MaxSalary,
TypeOf(Double),MAX(SALARY));
DataTable1.Columns.Add(AvgSalary,
TypeOf(Double),AVG(SALARY));
DataTable1.Columns.Add(Calculado,
TypeOf(string),FIRST_NAME + + LAST_NAME);
DataTable1.Columns.Add(Lookup,
TypeOf(string),Parent(EmployeeDeparment).DEPARTMENT);
{ configura DataBindings }
tbCount.DataBindings.Add(
Binding.Create(Text,DataTable1,Count));
tbMinSalary.DataBindings.Add(
Binding.Create(Text,DataTable1,MinSalary));
tbMaxSalary.DataBindings.Add(
Binding.Create(Text,DataTable1,MaxSalary));
tbAvgSalary.DataBindings.Add(
Binding.Create(Text,DataTable1,AvgSalary));
// adiciona colunas
dgcol := DataGridTextBoxColumn.Create;
dgcol.MappingName := Calculado;
dgcol.HeaderText := Calculado (First_Name+Last_Name);
dgcol.Width := 150;
DataGridTableStyle1.GridColumnStyles.Add(dgcol);
dgcol := DataGridTextBoxColumn.Create;

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.57

dgcol.MappingName := Lookup;
dgcol.HeaderText := Lookup (Department);
dgcol.Width := 200;
DataGridTableStyle1.GridColumnStyles.Add(dgcol);
end;

Aqui usamos o mtodo Add da propriedade Columns do DataTable para


adicionar uma nova coluna. O ltimo parmetro indica a expresso que se encarregar de obter o valor para o campo. Uma expresso pode ser algo do
tipo: COUNT(DEPT_NO), AVG(SALARY), MAX(SALARY) etc.
No exemplo contamos quantos registros existem na tabela, a mdia, menor
e maior salrio dos funcionrios listados. A seguir usamos o recurso de
DataBindings para exibir esses valores em TextBoxes. Tambm usamos o recurso de Expressions para simular um campo calculado (unindo em um nico
campo o Fist_name e Last_Name do empregado) e tambm um campo Lookup.
Nesse ltimo caso, obtemos a descrio do departamento do funcionrio atravs da relao configurada (simulando um lookup). Isso e, como temos o
DEPT_NO no DataTable atual, puxamos a descrio do departamento a
partir do outro DataTable, usando a relao configurada. Exatamente como
fazamos campos Lookup na VCL.
A figura abaixo mostra a aplicao em execuo, exibindo os valores para
os campos com Expressions:

Observe que no ltimo TextBox podemos digitar uma expresso customizada,


em tempo de execuo. Isso feito no evento Click do boto:
procedure TWinForm.Button1_Click(sender: System.Object; e: System.EventArgs);
begin
DataTable1.Columns.Add(Custom,
TypeOf(string),tbCustom.Text);
{ configura DataBindings }
tbCustomResult.DataBindings.Add(
Binding.Create(Text,DataTable1,Custom));
end;

16. DataSets tipados


Dando continuidade ao nosso curso sobre acesso a dados no Delphi for
.NET com ADO.NET e BDP, veremos neste artigo como utilizar um interessante recurso oferecido pelo ADO.NET, DataSets Tipados.
O que so DataSets tipados?

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.58

Quando configuramos um BdpDataAdapter e geramos um DataSet, estamos


utilizando para a manipulao dos dados em memria sempre as mesmas classes (DataSet, DataTable etc ). Para acessar nomes de campos, devemos acessar
a coleo Columns e referenciar colunas pelo nome (uma string) ou ainda pelo
ndice. Se precisar recuperar o valor de um campo em um registro, usamos a
coleo Rows e novamente acessamos o campo pelo nome. Veja um exemplo:
DataSet1.Tables[0].Rows[0][NOME_CLIENTE] := Guinther Pauli;

O ADO.NET permite que sejam criadas classes descendentes de DataSet,


ou seja, cada tabela mapeada em uma classe. Isso muito comum em
frameworks de persistncia, como o Bold e o ECO. Uma vantagem da
abordagem, claro, que estaremos trabalhando mais orientado a objetos do
que baseados em padres do sistema de BD relacional. Ou seja, a mesma
atribuio anterior ficaria algo como:
Cliente.Nome := Guinther Pauli;

Sendo mais especfico, deixaremos de usar colees para acessar campos


da tabela e passamos a tratar uma tabela com um Objeto. Os campos passam
a ser Atributos da nova classe, e tambm podemos incluir operaes sobre os
dados na mesma classe, como por exemplo, CalcularMedia, SomarPedidos
etc. Isso com certeza facilita muito a manuteno da sua aplicao, reforando
ainda princpios bsicos da orientao a objetos.
Uma outra vantagem que voc pode tirar vantagem do processo de compilao. Como os campos viram atributos, podemos usar o Code Insight para
acessar o nome dos campos. Se usssemos DataSets diretamente, o nome de
um campo digitado de forma incorreta s seria detectado em tempo de execuo.
Veja um exemplo:

Criando um DataSet tipado


Inicie uma nova aplicao do tipo Windows Forms Application. Expanda a
conexo Employee no Data Explorer e arraste a tabelas Department para o
designer. Isso cria um BdpConnection e um BdpDataAdapter. D um clique de
direita sobre o BdpDataAdapter e escolha a opo Generate Typed DataSet:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.59

Indique quais as tabelas devem ser usadas para criar a nova classe, nesse
caso somente uma:

Observe que ser criado um novo componente no designer, com o nome


padro DataSet11.

Observe que ao projeto so adicionados dois novos arquivos, um arquivo


XSD, que o XML schema representando o DataSet e uma unit, chamada
DataSet1Unit, que abriga a classe criada para o DataSet tipado:

Veja o schema XSD abaixo:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.60

.61

A listagem a seguir mostra o cdigo gerado para o DataSet tipado, observe


que os campos da tabela viraram atributos da classe:
//
// <autogenerated>
//
This code was generated by a tool.
//
Runtime Version: 1.1.4322.573
//
//
Changes to this file may cause incorrect behavior and will be lost if
//
the code is regenerated.
// </autogenerated>
//
//
// This source code was auto-generated by xsd, Version=1.1.4322.573.
//
unit DataSet1Unit;
interface
uses System.Data,
System.Xml,
System.Runtime.Serialization,
System.Collections,
System.ComponentModel,
System.Diagnostics,
System.Globalization, System.Xml.Schema, System.IO;
type
[Serializable]
[System.ComponentModel.DesignerCategoryAttribute(code)]
[System.Diagnostics.DebuggerStepThrough]
[System.ComponentModel.ToolboxItem(True)]
DataSet1 = class(DataSet)
public
type
DEPARTMENTRowChangeEvent = class;
DEPARTMENTRowChangeEventHandler = procedure(sender: System.Object; e: DEPARTMENTRowChangeEvent) of object;
DEPARTMENTRow = class;
[System.Diagnostics.DebuggerStepThrough]
DEPARTMENTDataTable = class(DataTable, System.Collections.IEnumerable)
strict private
columnDEPT_NO: DataColumn;
columnDEPARTMENT: DataColumn;

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

columnHEAD_DEPT: DataColumn;
columnMNGR_NO: DataColumn;
columnBUDGET: DataColumn;
columnLOCATION: DataColumn;
columnPHONE_NO: DataColumn;
public
DEPARTMENTRowChanged: DEPARTMENTRowChangeEventHandler;
DEPARTMENTRowChanging: DEPARTMENTRowChangeEventHandler;
DEPARTMENTRowDeleted: DEPARTMENTRowChangeEventHandler;
DEPARTMENTRowDeleting: DEPARTMENTRowChangeEventHandler;
private
constructor Create; overload;
constructor Create(table: DataTable); overload;
public
function get_Count: Integer;
private
function get_DEPT_NOColumn: DataColumn;
function get_DEPARTMENTColumn: DataColumn;
function get_HEAD_DEPTColumn: DataColumn;
function get_MNGR_NOColumn: DataColumn;
function get_BUDGETColumn: DataColumn;
function get_LOCATIONColumn: DataColumn;
function get_PHONE_NOColumn: DataColumn;
public
function get_Item(index: Integer): DEPARTMENTRow;
[System.ComponentModel.Browsable(False)]
property Count: Integer read get_Count;
private
property DEPT_NOColumn: DataColumn read get_DEPT_NOColumn;
property DEPARTMENTColumn: DataColumn read get_DEPARTMENTColumn;
property HEAD_DEPTColumn: DataColumn read get_HEAD_DEPTColumn;
property MNGR_NOColumn: DataColumn read get_MNGR_NOColumn;
property BUDGETColumn: DataColumn read get_BUDGETColumn;
property LOCATIONColumn: DataColumn read get_LOCATIONColumn;
property PHONE_NOColumn: DataColumn read get_PHONE_NOColumn;
public
property Item[index: Integer]: DEPARTMENTRow read get_Item;
procedure AddDEPARTMENTRow(row: DEPARTMENTRow); overload;
function AddDEPARTMENTRow(DEPT_NO: string; DEPARTMENT: string; HEAD_DEPT: string;
MNGR_NO: SmallInt; BUDGET: System.Double; LOCATION: string; PHONE_NO: string): DEPARTMENTRow; overload;
function FindByDEPT_NODEPARTMENT(DEPT_NO: string; DEPARTMENT: string): DEPARTMENTRow;
function GetEnumerator: System.Collections.IEnumerator;
function Clone: DataTable; override;
strict protected
function CreateInstance: DataTable; override;
private
procedure InitVars;
strict private
procedure InitClass;
public
function NewDEPARTMENTRow: DEPARTMENTRow;
strict protected
function NewRowFromBuilder(builder: DataRowBuilder): DataRow; override;
function GetRowType: System.Type; override;
procedure OnRowChanged(e: DataRowChangeEventArgs); override;
procedure OnRowChanging(e: DataRowChangeEventArgs); override;
procedure OnRowDeleted(e: DataRowChangeEventArgs); override;
procedure OnRowDeleting(e: DataRowChangeEventArgs); override;
public
procedure RemoveDEPARTMENTRow(row: DEPARTMENTRow);
end;

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.62

[System.Diagnostics.DebuggerStepThrough]
DEPARTMENTRow = class(DataRow)
strict private
tableDEPARTMENT: DEPARTMENTDataTable;
private
constructor Create(rb: DataRowBuilder);
public
function get_DEPT_NO: string;
function get_DEPARTMENT: string;
function get_HEAD_DEPT: string;
function get_MNGR_NO: SmallInt;
function get_BUDGET: System.Double;
function get_LOCATION: string;
function get_PHONE_NO: string;
procedure set_DEPT_NO(Value: string);
procedure set_DEPARTMENT(Value: string);
procedure set_HEAD_DEPT(Value: string);
procedure set_MNGR_NO(Value: SmallInt);
procedure set_BUDGET(Value: System.Double);
procedure set_LOCATION(Value: string);
procedure set_PHONE_NO(Value: string);
property DEPT_NO: string read get_DEPT_NO write set_DEPT_NO;
property DEPARTMENT: string read get_DEPARTMENT write set_DEPARTMENT;
property HEAD_DEPT: string read get_HEAD_DEPT write set_HEAD_DEPT;
property MNGR_NO: SmallInt read get_MNGR_NO write set_MNGR_NO;
property BUDGET: System.Double read get_BUDGET write set_BUDGET;
property LOCATION: string read get_LOCATION write set_LOCATION;
property PHONE_NO: string read get_PHONE_NO write set_PHONE_NO;
function IsHEAD_DEPTNull: Boolean;
procedure SetHEAD_DEPTNull;
function IsMNGR_NONull: Boolean;
procedure SetMNGR_NONull;
function IsBUDGETNull: Boolean;
procedure SetBUDGETNull;
function IsLOCATIONNull: Boolean;
procedure SetLOCATIONNull;
function IsPHONE_NONull: Boolean;
procedure SetPHONE_NONull;
end;
[System.Diagnostics.DebuggerStepThrough]
DEPARTMENTRowChangeEvent = class(EventArgs)
strict private
eventRow: DEPARTMENTRow;
eventAction: DataRowAction;
public
constructor Create(row: DEPARTMENTRow; action: DataRowAction);
function get_Row: DEPARTMENTRow;
function get_Action: DataRowAction;
property Row: DEPARTMENTRow read get_Row;
property Action: DataRowAction read get_Action;
end;
strict private
tableDEPARTMENT: DEPARTMENTDataTable;
public
constructor Create; overload;
strict protected
constructor Create(info: SerializationInfo; context: StreamingContext); overload;
public
function get_DEPARTMENT: DEPARTMENTDataTable;
[System.ComponentModel.Browsable(False)]
[System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Content)]
property DEPARTMENT: DEPARTMENTDataTable read get_DEPARTMENT;
function Clone: DataSet; override;
strict protected
function ShouldSerializeTables: Boolean; override;
function ShouldSerializeRelations: Boolean; override;
procedure ReadXmlSerializable(reader: XmlReader); override;

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.63

function GetSchemaSerializable: System.Xml.Schema.XmlSchema; override;


private
procedure InitVars;
strict private
procedure InitClass;
function ShouldSerializeDEPARTMENT: Boolean;
procedure SchemaChanged(sender: System.Object; e: System.ComponentModel.CollectionChangeEventArgs);
end;
implementation
{$AUTOBOX ON}
{$HINTS OFF}
{$WARNINGS OFF}
[assembly: RuntimeRequired(TypeOf(DataSet1))]
constructor DataSet1.DEPARTMENTDataTable.Create;
begin
inherited Create(DEPARTMENT);
Self.InitClass;
end;
constructor DataSet1.DEPARTMENTDataTable.Create(table: DataTable);
begin
inherited Create(table.TableName);
if (table.CaseSensitive <> table.DataSet.CaseSensitive) then
Self.CaseSensitive := table.CaseSensitive;
if (table.Locale.ToString <> table.DataSet.Locale.ToString) then
Self.Locale := table.Locale;
if (table.Namespace <> table.DataSet.Namespace) then
Self.Namespace := table.Namespace;
Self.Prefix := table.Prefix;
Self.MinimumCapacity := table.MinimumCapacity;
Self.DisplayExpression := table.DisplayExpression;
end;
function DataSet1.DEPARTMENTDataTable.get_Count: Integer;
begin
Result := Self.Rows.Count;
end;
function DataSet1.DEPARTMENTDataTable.get_DEPT_NOColumn: DataColumn;
begin
Result := Self.columnDEPT_NO;
end;
function DataSet1.DEPARTMENTDataTable.get_DEPARTMENTColumn: DataColumn;
begin
Result := Self.columnDEPARTMENT;
end;
function DataSet1.DEPARTMENTDataTable.get_HEAD_DEPTColumn: DataColumn;
begin
Result := Self.columnHEAD_DEPT;
end;
function DataSet1.DEPARTMENTDataTable.get_MNGR_NOColumn: DataColumn;
begin
Result := Self.columnMNGR_NO;
end;
function DataSet1.DEPARTMENTDataTable.get_BUDGETColumn: DataColumn;
begin
Result := Self.columnBUDGET;
end;
function DataSet1.DEPARTMENTDataTable.get_LOCATIONColumn: DataColumn;
begin
Result := Self.columnLOCATION;
end;
function DataSet1.DEPARTMENTDataTable.get_PHONE_NOColumn: DataColumn;
begin
Result := Self.columnPHONE_NO;
end;
function DataSet1.DEPARTMENTDataTable.get_Item(index: Integer): DEPARTMENTRow;
begin
Result := (DEPARTMENTRow(Self.Rows[index]));
end;
procedure DataSet1.DEPARTMENTDataTable.AddDEPARTMENTRow(row: DEPARTMENTRow);
begin
Self.Rows.Add(row);
end;
function DataSet1.DEPARTMENTDataTable.AddDEPARTMENTRow(DEPT_NO: string; DEPARTMENT: string;
HEAD_DEPT: string; MNGR_NO: SmallInt; BUDGET: System.Double; LOCATION: string;
PHONE_NO: string): DEPARTMENTRow;

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.64

type
TArrayOfSystem_Object = array of System.Object;
var
rowDEPARTMENTRow: DEPARTMENTRow;
begin
rowDEPARTMENTRow := (DEPARTMENTRow(Self.NewRow));
rowDEPARTMENTRow.ItemArray := TArrayOfSystem_Object.Create(DEPT_NO, DEPARTMENT,
HEAD_DEPT, MNGR_NO, BUDGET, LOCATION, PHONE_NO);
Self.Rows.Add(rowDEPARTMENTRow);
Result := rowDEPARTMENTRow;
end;
function DataSet1.DEPARTMENTDataTable.FindByDEPT_NODEPARTMENT(DEPT_NO: string;
DEPARTMENT: string): DEPARTMENTRow;
type
TArrayOfSystem_Object = array of System.Object;
begin
Result := (DEPARTMENTRow(Self.Rows.Find(TArrayOfSystem_Object.Create(DEPT_NO,
DEPARTMENT))));
end;
function DataSet1.DEPARTMENTDataTable.GetEnumerator: System.Collections.IEnumerator;
begin
Result := Self.Rows.GetEnumerator;
end;
function DataSet1.DEPARTMENTDataTable.Clone: DataTable;
var
cln: DEPARTMENTDataTable;
begin
cln := (DEPARTMENTDataTable(inherited Clone));
cln.InitVars;
Result := cln;
end;
function DataSet1.DEPARTMENTDataTable.CreateInstance: DataTable;
begin
Result := DEPARTMENTDataTable.Create;
end;
procedure DataSet1.DEPARTMENTDataTable.InitVars;
begin
Self.columnDEPT_NO := Self.Columns[DEPT_NO];
Self.columnDEPARTMENT := Self.Columns[DEPARTMENT];
Self.columnHEAD_DEPT := Self.Columns[HEAD_DEPT];
Self.columnMNGR_NO := Self.Columns[MNGR_NO];
Self.columnBUDGET := Self.Columns[BUDGET];
Self.columnLOCATION := Self.Columns[LOCATION];
Self.columnPHONE_NO := Self.Columns[PHONE_NO];
end;
procedure DataSet1.DEPARTMENTDataTable.InitClass;
type
TArrayOfDataColumn = array of DataColumn;
begin
Self.columnDEPT_NO := DataColumn.Create(DEPT_NO, TypeOf(string), nil, System.Data.MappingType.Element);
Self.Columns.Add(Self.columnDEPT_NO);
Self.columnDEPARTMENT := DataColumn.Create(DEPARTMENT, TypeOf(string), nil,
System.Data.MappingType.Element);
Self.Columns.Add(Self.columnDEPARTMENT);
Self.columnHEAD_DEPT := DataColumn.Create(HEAD_DEPT, TypeOf(string), nil,
System.Data.MappingType.Element);
Self.Columns.Add(Self.columnHEAD_DEPT);
Self.columnMNGR_NO := DataColumn.Create(MNGR_NO, TypeOf(SmallInt), nil, System.Data.MappingType.Element);
Self.Columns.Add(Self.columnMNGR_NO);
Self.columnBUDGET := DataColumn.Create(BUDGET, TypeOf(System.Double), nil,
System.Data.MappingType.Element);
Self.Columns.Add(Self.columnBUDGET);
Self.columnLOCATION := DataColumn.Create(LOCATION, TypeOf(string), nil, System.Data.MappingType.Element);
Self.Columns.Add(Self.columnLOCATION);
Self.columnPHONE_NO := DataColumn.Create(PHONE_NO, TypeOf(string), nil, System.Data.MappingType.Element);
Self.Columns.Add(Self.columnPHONE_NO);
Self.Constraints.Add(UniqueConstraint.Create(Constraint2, TArrayOfDataColumn.Create(Self.columnDEPT_NO,

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.65

Self.columnDEPARTMENT), True));
Self.Constraints.Add(UniqueConstraint.Create(Constraint1, TArrayOfDataColumn.Create(Self.columnDEPARTMENT),
False));
Self.columnDEPT_NO.AllowDBNull := False;
Self.columnDEPARTMENT.AllowDBNull := False;
Self.columnDEPARTMENT.Unique := True;
end;
function DataSet1.DEPARTMENTDataTable.NewDEPARTMENTRow: DEPARTMENTRow;
begin
Result := (DEPARTMENTRow(Self.NewRow));
end;
function DataSet1.DEPARTMENTDataTable.NewRowFromBuilder(builder: DataRowBuilder): DataRow;
begin
Result := DEPARTMENTRow.Create(builder);
end;
function DataSet1.DEPARTMENTDataTable.GetRowType: System.Type;
begin
Result := TypeOf(DEPARTMENTRow);
end;
procedure DataSet1.DEPARTMENTDataTable.OnRowChanged(e: DataRowChangeEventArgs);
begin
inherited OnRowChanged(e);
if (Assigned(Self.DEPARTMENTRowChanged)) then
Self.DEPARTMENTRowChanged(Self, DEPARTMENTRowChangeEvent.Create((DEPARTMENTRow(e.Row)),
e.Action));
end;
procedure DataSet1.DEPARTMENTDataTable.OnRowChanging(e: DataRowChangeEventArgs);
begin
inherited OnRowChanging(e);
if (Assigned(Self.DEPARTMENTRowChanging)) then
Self.DEPARTMENTRowChanging(Self, DEPARTMENTRowChangeEvent.Create((DEPARTMENTRow(e.Row)),
e.Action));
end;
procedure DataSet1.DEPARTMENTDataTable.OnRowDeleted(e: DataRowChangeEventArgs);
begin
inherited OnRowDeleted(e);
if (Assigned(Self.DEPARTMENTRowDeleted)) then
Self.DEPARTMENTRowDeleted(Self, DEPARTMENTRowChangeEvent.Create((DEPARTMENTRow(e.Row)),
e.Action));
end;
procedure DataSet1.DEPARTMENTDataTable.OnRowDeleting(e: DataRowChangeEventArgs);
begin
inherited OnRowDeleting(e);
if (Assigned(Self.DEPARTMENTRowDeleting)) then
Self.DEPARTMENTRowDeleting(Self, DEPARTMENTRowChangeEvent.Create((DEPARTMENTRow(e.Row)),
e.Action));
end;
procedure DataSet1.DEPARTMENTDataTable.RemoveDEPARTMENTRow(row: DEPARTMENTRow);
begin
Self.Rows.Remove(row);
end;
constructor DataSet1.DEPARTMENTRow.Create(rb: DataRowBuilder);
begin
inherited Create(rb);
Self.tableDEPARTMENT := (DEPARTMENTDataTable(Self.Table));
end;
function DataSet1.DEPARTMENTRow.get_DEPT_NO: string;
begin
Result := (string(Self[Self.tableDEPARTMENT.DEPT_NOColumn]));
end;
function DataSet1.DEPARTMENTRow.get_DEPARTMENT: string;
begin
Result := (string(Self[Self.tableDEPARTMENT.DEPARTMENTColumn]));
end;
function DataSet1.DEPARTMENTRow.get_HEAD_DEPT: string;
begin
try
Result := (string(Self[Self.tableDEPARTMENT.HEAD_DEPTColumn]));
Exit;
except
on e: InvalidCastException do

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.66

raise StrongTypingException.Create(Cannot get value because it is DBNull.,


e);
end;
end;
function DataSet1.DEPARTMENTRow.get_MNGR_NO: SmallInt;
begin
try
Result := (SmallInt(Self[Self.tableDEPARTMENT.MNGR_NOColumn]));
Exit;
except
on e: InvalidCastException do
raise StrongTypingException.Create(Cannot get value because it is DBNull.,
e);
end;
end;
function DataSet1.DEPARTMENTRow.get_BUDGET: System.Double;
begin
try
Result := (System.Double(Self[Self.tableDEPARTMENT.BUDGETColumn]));
Exit;
except
on e: InvalidCastException do
raise StrongTypingException.Create(Cannot get value because it is DBNull.,
e);
end;
end;
function DataSet1.DEPARTMENTRow.get_LOCATION: string;
begin
try
Result := (string(Self[Self.tableDEPARTMENT.LOCATIONColumn]));
Exit;
except
on e: InvalidCastException do
raise StrongTypingException.Create(Cannot get value because it is DBNull.,
e);
end;
end;
function DataSet1.DEPARTMENTRow.get_PHONE_NO: string;
begin
try
Result := (string(Self[Self.tableDEPARTMENT.PHONE_NOColumn]));
Exit;
except
on e: InvalidCastException do
raise StrongTypingException.Create(Cannot get value because it is DBNull.,
e);
end;
end;
procedure DataSet1.DEPARTMENTRow.set_DEPT_NO(Value: string);
begin
Self[Self.tableDEPARTMENT.DEPT_NOColumn] := Value;
end;
procedure DataSet1.DEPARTMENTRow.set_DEPARTMENT(Value: string);
begin
Self[Self.tableDEPARTMENT.DEPARTMENTColumn] := Value;
end;
procedure DataSet1.DEPARTMENTRow.set_HEAD_DEPT(Value: string);
begin
Self[Self.tableDEPARTMENT.HEAD_DEPTColumn] := Value;
end;
procedure DataSet1.DEPARTMENTRow.set_MNGR_NO(Value: SmallInt);
begin
Self[Self.tableDEPARTMENT.MNGR_NOColumn] := Value;
end;
procedure DataSet1.DEPARTMENTRow.set_BUDGET(Value: System.Double);
begin
Self[Self.tableDEPARTMENT.BUDGETColumn] := Value;
end;

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.67

procedure DataSet1.DEPARTMENTRow.set_LOCATION(Value: string);


begin
Self[Self.tableDEPARTMENT.LOCATIONColumn] := Value;
end;
procedure DataSet1.DEPARTMENTRow.set_PHONE_NO(Value: string);
begin
Self[Self.tableDEPARTMENT.PHONE_NOColumn] := Value;
end;
function DataSet1.DEPARTMENTRow.IsHEAD_DEPTNull: Boolean;
begin
Result := Self.IsNull(Self.tableDEPARTMENT.HEAD_DEPTColumn);
end;
procedure DataSet1.DEPARTMENTRow.SetHEAD_DEPTNull;
begin
Self[Self.tableDEPARTMENT.HEAD_DEPTColumn] := System.Convert.DBNull;
end;
function DataSet1.DEPARTMENTRow.IsMNGR_NONull: Boolean;
begin
Result := Self.IsNull(Self.tableDEPARTMENT.MNGR_NOColumn);
end;
procedure DataSet1.DEPARTMENTRow.SetMNGR_NONull;
begin
Self[Self.tableDEPARTMENT.MNGR_NOColumn] := System.Convert.DBNull;
end;
function DataSet1.DEPARTMENTRow.IsBUDGETNull: Boolean;
begin
Result := Self.IsNull(Self.tableDEPARTMENT.BUDGETColumn);
end;
procedure DataSet1.DEPARTMENTRow.SetBUDGETNull;
begin
Self[Self.tableDEPARTMENT.BUDGETColumn] := System.Convert.DBNull;
end;
function DataSet1.DEPARTMENTRow.IsLOCATIONNull: Boolean;
begin
Result := Self.IsNull(Self.tableDEPARTMENT.LOCATIONColumn);
end;
procedure DataSet1.DEPARTMENTRow.SetLOCATIONNull;
begin
Self[Self.tableDEPARTMENT.LOCATIONColumn] := System.Convert.DBNull;
end;
function DataSet1.DEPARTMENTRow.IsPHONE_NONull: Boolean;
begin
Result := Self.IsNull(Self.tableDEPARTMENT.PHONE_NOColumn);
end;
procedure DataSet1.DEPARTMENTRow.SetPHONE_NONull;
begin
Self[Self.tableDEPARTMENT.PHONE_NOColumn] := System.Convert.DBNull;
end;
constructor DataSet1.DEPARTMENTRowChangeEvent.Create(row: DEPARTMENTRow;
action: DataRowAction);
begin
inherited Create;
Self.eventRow := row;
Self.eventAction := action;
end;
function DataSet1.DEPARTMENTRowChangeEvent.get_Row: DEPARTMENTRow;
begin
Result := Self.eventRow;
end;
function DataSet1.DEPARTMENTRowChangeEvent.get_Action: DataRowAction;
begin
Result := Self.eventAction;
end;
constructor DataSet1.Create;
var
schemaChangedHandler: System.ComponentModel.CollectionChangeEventHandler;
begin
inherited Create;
Self.InitClass;
schemaChangedHandler := Self.SchemaChanged;
Include(Self.Tables.CollectionChanged, schemaChangedHandler);

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.68

Include(Self.Relations.CollectionChanged, schemaChangedHandler);
end;
constructor DataSet1.Create(info: SerializationInfo; context: StreamingContext);
var
schemaChangedHandler: System.ComponentModel.CollectionChangeEventHandler;
ds: DataSet;
strSchema: string;
begin
inherited Create;
strSchema := (string(info.GetValue(XmlSchema, TypeOf(string))));
if (strSchema <> nil) then
begin
ds := DataSet.Create;
ds.ReadXmlSchema(XmlTextReader.Create(System.IO.StringReader.Create(strSchema)));
if (ds.Tables[DEPARTMENT] <> nil) then
Self.Tables.Add(DEPARTMENTDataTable.Create(ds.Tables[DEPARTMENT]));
Self.DataSetName := ds.DataSetName;
Self.Prefix := ds.Prefix;
Self.Namespace := ds.Namespace;
Self.Locale := ds.Locale;
Self.CaseSensitive := ds.CaseSensitive;
Self.EnforceConstraints := ds.EnforceConstraints;
Self.Merge(ds, False, System.Data.MissingSchemaAction.Add);
Self.InitVars;
end
else
Self.InitClass;
Self.GetSerializationData(info, context);
schemaChangedHandler := Self.SchemaChanged;
Include(Self.Tables.CollectionChanged, schemaChangedHandler);
Include(Self.Relations.CollectionChanged, schemaChangedHandler);
end;
function DataSet1.get_DEPARTMENT: DEPARTMENTDataTable;
begin
Result := Self.tableDEPARTMENT;
end;
function DataSet1.Clone: DataSet;
var
cln: DataSet1;
begin
cln := (DataSet1(inherited Clone));
cln.InitVars;
Result := cln;
end;
function DataSet1.ShouldSerializeTables: Boolean;
begin
Result := False;
end;
function DataSet1.ShouldSerializeRelations: Boolean;
begin
Result := False;
end;
procedure DataSet1.ReadXmlSerializable(reader: XmlReader);
var
ds: DataSet;
begin
Self.Reset;
ds := DataSet.Create;
ds.ReadXml(reader);
if (ds.Tables[DEPARTMENT] <> nil) then
Self.Tables.Add(DEPARTMENTDataTable.Create(ds.Tables[DEPARTMENT]));
Self.DataSetName := ds.DataSetName;
Self.Prefix := ds.Prefix;
Self.Namespace := ds.Namespace;
Self.Locale := ds.Locale;
Self.CaseSensitive := ds.CaseSensitive;
Self.EnforceConstraints := ds.EnforceConstraints;
Self.Merge(ds, False, System.Data.MissingSchemaAction.Add);
Self.InitVars;
end;
function DataSet1.GetSchemaSerializable: System.Xml.Schema.XmlSchema;
var

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.69

stream: System.IO.MemoryStream;
begin
stream := System.IO.MemoryStream.Create;
Self.WriteXmlSchema(XmlTextWriter.Create(stream,
stream.Position := 0;

nil));

Result := System.Xml.Schema.XmlSchema.Read(XmlTextReader.Create(stream), nil);


end;
procedure DataSet1.InitVars;
begin
Self.tableDEPARTMENT := (DEPARTMENTDataTable(Self.Tables[DEPARTMENT]));
if (Self.tableDEPARTMENT <> nil) then
Self.tableDEPARTMENT.InitVars;
end;
procedure DataSet1.InitClass;
begin
Self.DataSetName := DataSet1;
Self.Prefix := ;
Self.Namespace := http://www.changeme.now/DataSet1.xsd;
Self.Locale := System.Globalization.CultureInfo.Create(pt-BR);
Self.CaseSensitive := False;
Self.EnforceConstraints := True;
Self.tableDEPARTMENT := DEPARTMENTDataTable.Create;
Self.Tables.Add(Self.tableDEPARTMENT);
end;
function DataSet1.ShouldSerializeDEPARTMENT: Boolean;
begin
Result := False;
end;
procedure DataSet1.SchemaChanged(sender: System.Object; e: System.ComponentModel.CollectionChangeEventArgs);
begin
if (e.Action = System.ComponentModel.CollectionChangeAction.Remove) then
Self.InitVars;
end;
end.

Usando o novo DataSet tipado


Coloque no formulrio principal um DataGrid e aponte seu DataSource para
DataSet11.DEPARTMENT. Coloque um Button e no seu evento Click digite:
procedure TWinForm1.Button1_Click(sender: System.Object; e: System.EventArgs);
var
Dep: DataSet1Unit.DataSet1.DEPARTMENTRow;
begin
Dep := DataSet11.DEPARTMENT.NewDEPARTMENTRow;
Dep.DEPT_NO := 1000;
Dep.DEPARTMENT := Isso bem mais OO!;
Dep.LOCATION := Delphi 2005 rocks!;
DataSet11.DEPARTMENT.AddDEPARTMENTRow(Dep);
end;

Aqui usamos os novos tipos declarados na unit para o DataSet gerado,


adicionamos um Row e configuramos os valores para seus campos, e a seguir
inclumos no DataTable Department do Datset.
Execute e teste a aplicao. Veja abaixo o novo registro inserido:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.70

17. Trafegando DataSets via Web Services


Quando comeamos a estudar o ADO.NET neste curso, voc deve lembrar
que DataSets podem ser completamente expressos em formato XML. Dessa
forma, fica muito simples trafegar dados pela Web ou mesmo atravs de diferentes dispositivos. Podemos, por exemplo, oferece consultas a um BD atravs
de qualquer ponto da Internet que possa acessar nosso Web Service, sem que
seja necessrio qualquer tipo de configurao extra, instalao de bibliotecas,
configurao de acesso ao BD etc. Tudo feito no servidor, o cliente apensar
faz requisies SOAP e recebe dados em XML.
Criando o servidor
Para ver como isso funciona na prtica, no Delphi 2005, crie uma nova
aplicao Web Service chamada AppDados.

D o nome de WSDados para a classe do Web Service e Dados para o


arquivo asmx.

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.71

Coloque no Designer um BdpConnection apontando para o banco Employee,


um
BdpDataAdater
e
um
DataSet.
Aponte
o
BdpDataAdater.SelectCommand.Connection para o BdpConnection1 e o
BdpDataAdater1.DataSet para o DataSet1.
No editor, clique na aba Code e adicione o seguinte mtodo classe criada:

[WebMethod]
function Execute(SQL: string): DataSet;

O atributo WebMethod indica que o mtodo de Web Service de poder


ser invocado remotamente. Implemente-o conforme mostrado a seguir:
function TWebService1.Execute(SQL: string): DataSet;
begin
BdpDataAdapter1.SelectCommand.CommandText := SQL;
BdpDataAdapter1.Active := True;
result := DataSet1;
end;

O mtodo anterior recebe uma instruo Select por parmetro, processa no


servidor Interbase e devolve o resultado em formato XML. Observe que o
resultado da funo um DataSet do ADO.NET, que automaticamente
serializado para envio atravs de http.
Consumindo Web Services em uma aplicao Windows Forms
Faremos dois exemplo simples para consumir o Web Service anterior, um
do tipo Windows Forms e outro do tipo Web Forms.
No Delphi 2005, inicie um nova aplicao Windows Forms:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.72

Coloque no formulrio principal os seguintes componentes: TextBox, Button


e DataGrid.

Referenciando o Web Service


Vamos adicionar uma referncia ao Web Service criado anteriormente. No
Project Manager clique de direita no nome do assembly e escolha Add Web
Reference:

Na janela que aparece (o UDDI browser da IDE) coloque a URL do Web


Service:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.73

.74

Clique em Add Reference para adicionar a referncia ao Web Service e


criar as classes proxy. Seu Project Manager deve ficar assim:

A unit localhost.Dados.pas contm a classe proxy, que se encarrega de mapear


as chamadas locais para o servidor remoto. Isso permite que faamos chamadas ao Web Service como se fossem de um objeto local.
No evento Click do boto digite:
uses localhost.Dados;
...
var
ws: localhost.Dados.WSDados;
begin
ws := WSDados.Create;
DataGrid1.DataSource := ws.Execute(TextBox1.Text);
end;

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

Execute e teste a aplicao digitando um comando Select no TextBox. A


instruo enviada ao servidor, que processo a consulta devolve os dados em
XML:

Consumindo Web Services em uma aplicao ASP.NET Web Forms


A verso Web Forms do exemplo praticamente idntica, basta colocar os
Web Controls em um formulrio de uma nova aplicao ASP.NET, adicionar a
Web Reference e digitar o mesmo cdigo no evento Click do boto, chamando
a seguir o mtodo DataBind.

18. BDP e .NET Remoting


Neste artigo damos continuidade ao nosso curso sobre ADO.NET e veremos como usar os novos componentes do BDP adicionados no Delphi 2005.
Novos componentes do BDP
O BDP teve excelentes melhorias no Delphi 2005. Foram adicionados novos componentes, conforme mostrado na figura a seguir:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.75

O BdpCopyTable tambm novo e ser discutido em futuros artigos.


Os novos componentes DataSync
duas funcionalidades:

e DataHub

.tem basicamente

* Permitir a criao de aplicaes de DataSets distribudos (imagine isso


como um DataSnap para ADO.NET usando .NET Remoting);
* Adicionar suporte a Live-Data para outros providers do ADO.NET!
isso mesmo, o recurso de Live-Data, que permite a visualizao de dados em
controles em tempo de design (antes s suportado pelo BDP), agora pode ser
usado com os demais providers para ADO.NET (SQL Server, Oracle, OleDb,
Odbc, Firebird etc.)
Live-Data no mundo no-BDP
Vejamos como isso funciona: o DataSync gerencia uma coleo de
DataAdapters, que podem ser de qualquer provider (SqlDataAdapter,
OleDbDataAdapter etc.). Basta coloc-lo no formulrio e referenciar os
DataAdapters na propriedade Providers. A seguir, voc pode colocar um
DataHub, apontar seu DataPort para o DataSync, referenciar um DataSet e
ativ-lo. Pronto! Agora voc tem Live-Data para qualquer provider ADO.NET.
O DataHub funciona como uma ponte entre o DataSync e um DataSet. A
figura a seguir mostra o recurso utilizado em uma aplicao ASP.NET usando o
provider do ADO.NET para SQL Server:

BDP/ADO.NET e .NET Remoting


A utilizao desses componentes ainda mais poderosa para a construo
de aplicaes distribudas. A Borland criou para o ADO.NET algo muito semelhante ao que temos hoje no DataSnap. Isso possvel utilizando os dois componentes mostrados anteriormente em conjunto com os novos componentes
RemoteServer e RemoteConnection .
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.76

Criando o servidor
Primeiro preciso criar a aplicao servidora e configurar normalmente uma
conexo ao banco de dados (usando o BDP ou outro provider). Voe pode usar
para isso uma aplicao Windows Forms. Adicione alguns DataAdapters para
consultar / atualizar tabelas do BD. Colocamos um DataSync e na sua propriedade Providers referenciamos os DataAdapters.

Colocamos um RemoteServer, configuramos o ChannelType (Tcp ou Http),


a porta, o DataSync (apontando para o DataSync1) e AutoStart para True.

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 DataSetProvider, que prov acesso remoto aos DataAdapters
(que so semelhantes s Queries da VCL).
Veja como ficou o servidor:

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.77

Criando o cliente
No lado cliente da aplicao, que tambm ser do tipo Windows Forms,
usamos um RemoteConnection para conectar ao servidor anterior (que deve
permanecer em execuo). Configuramos as propriedades ChannelType, Port,
ProviderType (de acordo com os valores configurados no servidor) e URI para
RemoteServer1.

Para enxergar os DataAdapters do servidor referenciados pelo DataSync,


colocamos um DataHub e configuramos seu DataPort para RemoteConnection1,
apontamos para um DataSet e o ativamos. Pronto! Temos agora acesso aos
DataAdapters do servidor remoto.

Com isso possvel criar aplicaes distribudas de BD usando ADO.NET,


de forma muito semelhante ao que estvamos acostumados com o MIDAS /
DataSnap. A comunicao e troca de dados entre cliente e servidor feita
usando o .NET Remoting. E observe que para isso no precisamos digitar nenhuma linha de cdigo, e ainda existe o suporte a Live-Data em aplicaes
multicamadas.

19. BdpCopyTable
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.78

Neste artigo damos continuidade ao nosso curso sobre ADO.NET e veremos como usar o novo componente BdpCopyTable do BDP do Delphi 2005,
usado para copiar dados entre banco de dados. Esse componente usado
internamente pela IDE do Delphi 2005, atravs do utilitrio de migrao disponvel no Data Explorer. simples us-lo em aplicaes .NET, como veremos
aqui.
Criando a aplicao Windows Forms
Inicie uma nova aplicao Windows Forms no Delphi 2005.

Conexes
preciso que voc tenha configurado pelo menos duas conexes no Data
Explorer para testar o componente. Em minha mquina tenho duas conexes
configuradas, uma para acesso ao banco Employee do Interbase e outra para o
Northwind do SQL Server. Em partes anteriores do curso j falamos sobre
criao de conexes BDP. Neste exemplo, faremos a migrao da tabela Country
do IB para o SQL Server.

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.79

Configurando os componentes
Arraste a conexo Employee e Northwind para o formulrio, o que criar
dois BdpConnections. Coloque um BdpCommand, aponte para a conexo ao
IB e em CommandText digite a consulta a uma tabela: select * from
COUNTRY. Observe que com isso voc pode especificar condies para os
dados que sero migrados.
Coloque no formulrio o novo componente BdpCopyTable:

Configure suas propriedades conforme mostrado a seguir:

Destination a conexo (Banco) de destino dos dados. DestinationTable


o nome da nova tabela a ser criada no banco destino. SourceCommand o
BdpCommand que contm o comando que obtm os dados.

Migrando...
Coloque um Button no formulrio e no seu evento Click digite:
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.80

procedure TWinForm1.Button1_Click(sender: System.Object; e: System.EventArgs);


begin
BdpCopyTable1.Copy;
end;

Execute e teste a aplicao. A figura abaixo comprova que a tabela foi migrada com sucesso.

Com isso, fica muito simples migrar suas tabelas de um banco de dados para
outro. O BDP se encarrega de criar a estrutura e tambm migrar os dados,
conforme vimos.

20. Usando Stored Procedures


Nesta ltima parte do curso de BDP, veremos como usar Stored Procedures
com o Delphi 2005 e ADO.NET.
O que so Stored Procedures
Normalmente, em uma estrutura client/server, o hardware do servidor sempre ser o mais robusto, ento podemos utilizar esse hardware para realizar o
processamento mais pesado da aplicao, como insero, atualizao e excluso dos dados atravs de Stored Procedures (SP) ou mesmo para realizar
clculos mais complexos.
Stored Procedures so basicamente comandos SQL que residem no servidor. A aplicao cliente se encarrega de passar os valores para seus parmetros
e executar o procedimento. O servidor pode ento otimizar os planos de execuo, garantindo que Stored Procedures sejam processadas de forma cada
vez mais rpida. Ele faz isso preparando e pr-compilando procedimentos, pois
somente os parmetros (e no toda a instruo SQL) so passados do cliente
para o servidor.
Criando uma Stored Procedure

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.81

No cdigo a seguir temos um exemplo de criao de uma SP que retorna em


um parmetro de OUTPUT o total de vendas (Sales) realizadas por determinado empregado, passado no parmetro de INPUT (Emp_No):

create procedure TOTAL_VENDAS_EMPREGADO (


EMP_NO smallint)
returns (TOTAL numeric(15,2))
as begin
select sum(TOTAL_VALUE) from SALES
where SALES_REP = :EMP_NO into :TOTAL;
end

Para testar esse exemplo, crie essa SP no banco de dados Employee.gdb


que distribudo juntamente com o Interbase. Voc pode usar o IBConsole
para executar o comando.
Acessando a Stored Procedure
No Delphi 2005, inicie uma nova aplicao do tipo Windows Forms
Application Delphi for .NET. Arraste a conexo Employee a partir do
DataExplorer, para criar um BdpConnection automaticamente.

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.82

Coloque um componente BdpCommand, aponte sua propriedade Connection

para o BdpConnection1 e CommandType para StoredProcedure. Observe que


a propriedade CommandText se ajusta agora para receber o nome da
StoredProcedure
a
ser
executada.
Escolha
TOTAL_VENDAS_EMPREGADO.
Observe que a propriedade Params j lista os dois parmetros, de INPUT
e OUTPUT.
Executando a Stored Procedure
Coloque um Button, TextBox e uma Label no formulrio:
No evento Click do boto digite:
procedure TWinForm.Button1_Click(sender: System.Object; e: System.EventArgs);
begin

BdpConnection1.Open();
try
BdpCommand1.Parameters[0].Value := TextBox1.Text;
BdpCommand1.ExecuteNonQuery();
Label1.Text := Convert.ToString(BdpCommand1.Parameters[1].Value);
finally
BdpConnection1.Close();
end;
end;

No cdigo anterior, configuramos o parmetro de entrada (INPUT) da SP,


nesse caso o cdigo do empregado (EMP_NO) a partir do valor digitado no
TextBox. A seguir, usamos ExecuteNonQuery para processar a SP no servidor
e obtemos o valor de retorno a partir do parmetro de OUTPUT da SP. O
valor exibido na Label:
Concluses

ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP


Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.83

Espero que este curso de BDP e ADO.NET tenha ajudado voc


desenvolvedor a conhecer um pouco mais sobre essa importante e robusta
tecnologia de acesso a dados no Delphi for .NET. Foram 20 partes, onde vimos em detalhes todos os recursos da tecnologia, incluindo arquitetura, conexo, acesso, manipulao de dados, uso de DataSets, DataBindings, ADO.NET
com .NET Remoting e Web Services, novos componentes do Delphi 2005 e
muito mais. Sinta-se a vontade para entrar em contato comigo pelo e-mail a
seguir, para enviar crticas, dvidas e sugestes. Desejo sucesso a todos nos
projetos com o .NET Framework! Um abrao a todos e at a prxima!
. . .

Download
Voc pode fazer download de todos os exemplos deste curso a partir do endereo cc.borland.com/cc/ccweb.exe/author?authorid=222668

Guinther Pauli DelphiMan tem 10 anos de experincia em Delphi, autor de


mais de 100 artigos publicados na rea e do livro Delphi Programao para
Banco de Dados e Web, certificado oficial Delphi Advanced pela Borland
US, Delphi Web Development e Kylix Product. Ministrou palestras por todo o
pas, incluindo Borland Conference e ClubeDelphi Tech Day. editor geral da
revista ClubeDelphi e co-editor da Revista Web Mobile Magazine. Pode ser
contatado pelo endereo guinther_pauli@hotmail.com ou
guinther@clubedelphi.net
ClubeDelphi Curso de acesso a dados no Delphi 2005 ADO.NET e BDP
Guinther Pauli DevMedia - guinther@clubedelphi.net www.clubedelphi.net
Todos os direitos reservados

.84