segunda-feira, 23 de abril de 2007

Aplicações Robustas IV: Propagando de Exceções


Esse artigo é continuação do post “Aplicações Robustas III

Dando continuidade a seqüência de artigos sobre tratamento de exceções.....

Neste artigo vamos tratar de como acontece a propagação de exceções. Já falamos anteriormente sem detalhamos muito, vamos agora testar alguns exemplos e observar mais de perto como isso acontece. Consequentimente estaremos apurando a tecnica de tratamento de exceções.

Propagando exceções:

Suponha que uma exceção vemha ocorrer dentro de uma função “A”, essa função foi chamada pela procedure “B”, que por sua vez foi executada no evento OnClick de um botão. De cara vc pode perceber o problema envolvido se vc tiver um tratamento já na função “A”. Como propar essa exceção? Algumas vezes, acredite isso contece, vc terá que propagar essa exceção até a achamda mais externa. Vamos para a prática ...

Construindo um exemplo:

Inicie uma nova aplicação no Delphi:

No D7 - Menu, File ►New ► Application.
No BDS - Menu, File ►New ► VCL Forms Application – Delphi for Win 32 (“Alt, n, r”).

Adicione uma nova Unit: Nela vamos copdificar uma função.

No D7
- Menu, File ►New ► Unit.
No BDS - Menu, File ►New ► Unit – Delphi for Win 32 (“Alt, n, t”).

Eu vou salvar a Unit com o nome de “ZnFunctionPropagExcept”, o nome da unit do Form1 vou salvar como “ZnPropagExceptForm.pas”. Veja o Código da Unit:

unit ZnFunctionPropagExcept;

interface
uses DbClient, Db, Classes, SysUtils, Dialogs;

type
ZnException = class(Exception);

function SetDados(Lista: TStringList;
Cds: TClientDataSet; AField: TField): String;

procedure ForcaExcecao(const Value: string);

implementation

procedure ForcaExcecao(const Value: string);
begin
(* força uma exceção. Basta que um dos elementos da lista seja a palavra
exceção. *)
if AnsiUpperCase(Value) = AnsiUpperCase('exceção') then
raise ZnException.Create('ZnException: Buuuuuuuhhhhhhhh!!!!!!');
end;

function SetDados(Lista: TStringList;
Cds: TClientDataSet; AField: TField): String;
var
I: Integer;
begin
try
if Lista.Count <= 0 then
raise
Exception.Create('Nenhum elemento na lista.');
for I := 0 to Lista.Count - 1 do
begin
ForcaExcecao(Lista[i]);
if not Cds.Locate(AField.FieldName, Lista[i], []) then
begin
Cds.Append;
AField.AsString := Lista[i];
Cds.Post;
end;
end;
except
(*essa exceção não vou propagar*)
on E: ZnException do
MessageDlg(E.Message, mtInformation, [mbOK], 0);
(*Propagando a exceçaõ genérica*)
on E: Exception do raise;
end;
Result := Cds.XMLData;// retorna os dados no formato Xml numa string.
end;
end.



Na seção “uses” declaramos as seguintes bibliotecas: DbClient (para usarmos o TClientDataSet), Db (para usarmos o TField), Classes (para usarmos TStringList), SysUtils (onde está definida Exception), Dialogs (para usarmos a função “MessageDlg”).
Na seção “type” declaramos uma exceção “ZnException” aqual usaremos no nosso tratamento. Os dois modulos cdificados nesta Unit são: A função “SetDados” que recupera os dados de uma lista e atribui a um dataset, retorna um string que usaremos como log do que foi adicionado no dataset. O procedimento “ForcaExcecao”, como o próprio nome já indica força um exceção, algo que é impressindível para o nosso exemplo. Após codificar esta Unit, passe para a implentação do form1 na unit “ZnPropagExceptForm”.


No Form1 adicione os seguintes componentes: Um Edit (TEdit), um Button (TButton), um Label (Tlabe) e um ListBox (TListBox), da palheta Standard; Dois DbGrids (TDbGrid), da palheta Data Control; Dois DataSource(TDataSource), Dois ClientDataSet(TClientDataSet) Data access. Configure as propriedades dos componentes segundo listado abaixo:


Form1: TZnPropagExceptFrm
Name = ZnPropagExceptFrm
ClientHeight = 395
ClientWidth = 578
Caption = 'Propagando Exceções'

Label1: TLabel
Left = 16
Top = 8
Width = 31
Height = 13
Caption = 'Digite o Dado'

ListBox1: TListBox
Name = LstDados
Left = 16
Top = 56
Width = 121
Height = 329

Button1 : TButton
Name = BtnAddElemento
Left = 143
Top = 27
Width = 90
Height = 25
Caption = 'Add Elemento'

Edit1: TEdit
Name = EdtDados
Left = 16
Top = 29
Width = 121
Height = 21

ClientDataSet1: TClientDataSet
Name = cdsLog

ClientDataSet2: TClientDataSet
Name = cdsDados

DataSource1: TDataSource
Name = dsDados
DataSet = cdsDados

DataSource: TDataSource
Name = dsLog
DataSet = cdsLog

DbGrid1: TDBGrid
Name = GrdLog
Left = 248
Top = 216
Width = 322
Height = 169
DataSource = dsLog

DbGrid2: TDBGrid
Name = GrdDados
Left = 247
Top = 29
Width = 322
Height = 181
DataSource = dsDados

Button2: TButton
Name = BtnTransDados
Left = 143
Top = 70
Width = 90
Height = 25



A seção type da unit1 deverá estar assim

type
TZnPropagExceptFrm = class(TForm)
LstDados: TListBox;
BtnAddElemento: TButton;
EdtDados: TEdit;
Label1: TLabel;
dsDados: TDataSource;
cdsLog: TClientDataSet;
GrdLog: TDBGrid;
GrdDados: TDBGrid;
cdsDados: TClientDataSet;
dsLog: TDataSource;
BtnTransDados: TButton;
private
{ Private declarations }
public
{ Public declarations }
end;


Campos persistentes:

Vamos adicionar um campo persistente em cada um dos ClientDataSets. O Nome do Campo vai ser “ZnTexto”, o tipo string, e tamanho 200 (quantidade de caracteres). Click com o botão direito do mouse sobre o ClientDataSet, escolha aopção, “Fields Editor”.




Agora botão direito no Field Editor, escolha a opção, “New Field”.



Cofigure conforme a figura abaixo e click “Ok”:



Pronto vc acabou de criar um campo persistente num dataset. Repita a mesma operação para o outro ClientDataSet.



Codificando o evento OnClick do botão “BtnAddElemento”, “BtnAddElementoClick”:


procedure TZnPropagExceptFrm.BtnAddElementoClick(Sender: TObject);
begin
(* Atribuido a lista o valor digitado no Edtdados *)
LstDados.Items.Add(EdtDados.Text);
EdtDados.Clear; // Limpa o Edit
EdtDados.SetFocus;// seta o foco para ele
end;

É chegado o momento de usarmos a Unit contendo os módulos que programamos onde está o tratamento de exceção. Faça use unit, ou seja no Menu, File ►Use Unit (Alt, f, u), ou Alt + F11.



Codificando o evento OnClick do botão “BtnTransDado”, “BtnTransDadosClick”:

procedure TZnPropagExceptFrm.BtnTransDadosClick(Sender: TObject);
var
Lst: TStringList;
begin
Lst := TStringList.Create;
(*Bloco protegido*)
try
try
Lst.Assign(LstDados.Items);
cdsLog.XMLData := SetDados(Lst, cdsDados, cdsDadosZnTexto);
except
(*Capturando a exceção propagada pela rotina "SetDados"*)
on Zn: Exception do
begin
Self.Caption := Zn.Message + '/'+ Zn.ClassName;
MessageBox(0, 'Exceção propagada capturada!!!!',
'Estação Zn', MB_ICONASTERISK or MB_OK);
end;
end;
finally
Lst.Free;
end;

end;


Vamos executar nosso programa para testar. Eu garanto que vai ocorrer uma exceção, não programe uma linha de código além do que foi proposto até aqui. Antes de qualquer coisa, vamos preparar a IDE para não interromper a execução do programa caso aja uma exceção.
No D7:
No menu Tools ► Debuggrer Options ► Aba “Language Exceptions”, desmarque a check “Stop on Delphi Exception” (Dica do Felipe)
No BDS:
No menu Tools ►Options ...Selecione, na treeview a esquerda, Borland Debugger, sub item, language Exceptions, e desmarque a check “Notify on Language Exceptions”. Veja na figura abaixo:



Execute, pressionado F9, adiciono alguns elementos clickando em “add Elemento” e click em “Transfere Dados”. Se vc esta sincronisado comigo vai ocorrer, propositalmente, o seguinte: Uma mensagem será exibida “Exceção propagada capturada!!!!”, e no caption do form deve estar a mensagem da exceção e o tipo da exceção, que será “EDatabaseError”.



Eu deixei o ClientDataSet fechado de propósito para forçarmos uma exceção diferente da “ZnException”, definida na Unit “ZnFunctionPropagExcept”. Pois de acordo com o que codificamos ela será a única que não será propagada. Portanto, nesse primeiro teste experientamos a propagaçao da exceção. Como o DataSet estava fechado, ao tentar executar o metodo “Locate”, foi levantada uma execeção “EDatabaseError”. A prova de que ela foi propagada foi a execução dos comandos da setrutura “on ..do” no except do evento OnClick botão “BtnTransDado”: A atribuição ao caption do Form, e a execução da função “MessageBox”.
Prosseguindo com o nosso exemplo, vamos acrescentar um botão que irá abrir os ClientDataSets. Ficando ao critério do usuário forçar a exceção “EDatabaseError” ou não. Configure o novo botão da seguinte forma: Name = btnOpenCds; Caption = “Ativar Cds”; Top = 108; left =143; Width = 90.



procedure TZnPropagExceptFrm.BtnOpenCdsClick(Sender: TObject);
begin
(*O Método "CreateDataSet nos permite trabalhar com o ClienteDataSet
desconectado do banco de dados. "*)
cdsLog.CreateDataSet;
cdsDados.CreateDataSet;
end;

Vamos executar nosso segundo teste. Pressione F9, ative os datasets clickando no novo botão. Adicione alguns elementos. Lembre-se, se vc não adicionar a palavra “Exceção”, nenhuma exceção será levantada. Portanto, para testar o caso da exceção que não é propagada, adicione a palavra “exceçao”. E Clicke sobre o botão “Transfere Dados”.



Note na figura acima que o processo transferiu alguns elementos, mas quando encontrou a palvra “Exceçao”, foi interrompido e levantou a exceção. Contudo, essa exceção, não foi capturada pelo tratamento do botão “Transfere Dados”.
Verifique o codigo da função “SetDados”, veja que o que diferenciou um caso do outro foi a utilização da paralra reservada “raise” na estutura “on ..do” (que verificava se a exceção era do tipo “ZnException”). Quando vc trata uma exceção, a estrutura “try ... except” provê para vc um manipulador de exceções. Esse manipulador gerencia também o “tempo de existência” dessa exceção. Antes do final da estrutura a exceção “morre”, é desalocada. Assim sendo, se vc deseja que ela seja atribuida, passada, ao próximo manipulador, vc tem que usar a palavra reservadda “raise”.
Note Ainda que, assim que vc clickar no ok da mensagem da exceção, o processo continuará a execução após a estrutura “try ... except” da função “SetDados” e efetuará o log dos elementos transferidos para o “cdsDados”.



Nenhum comentário:

Postar um comentário