VB .NET - Estratégia para Tratamento de exceções


Qual a melhor estratégia para efetuar o tratamento de exceções em uma aplicação ? Quais exceções devo utilizar ? Como efetuar um tratamento de exceções eficiente ?

Se você ainda tem estas dúvidas este artigo se propõe a mostrar como você pode montar uma estratégia de tratamento de exceções para aplicações VB.NET. Antes de prosseguir sugiro que você  leia os artigos já publicados no site, se o assunto for novo para você.

Recordando rapidamente alguns conceitos básicos

O tratamento de exceções é implementado usando a estrutura de comandos :  Try.. Catch...Finally..End Try cuja sintaxe básica é a seguinte:

Try
   ' código que pode gerar/disparar uma exceção
Catch
   ' código que efetua o tratamento da exceção
Finally
   ' efetua a limpeza do código (fechamento de conexões, destruição de objetos usados , etc...)
End Try

As instruções Try/End Try são obrigatórias. As cláusulas Catch/Finally podem ser usadas no interior do bloco Try/End Try, mas pelo menos uma delas é de uso obrigatório. Você pode ter mais de um Catch no interior do bloco.

Você pode usar as exceções padrão que a plataforma .NET  contém; todas elas derivam da classe base System.Exception. As exceções mais comuns estão listadas abaixo:

  • ArgumentException— argumento inválido de passado ao um método
  • ArgumentNullException— utilizados por métodos que não permitem argumentos com valores null.
  • DivideByZeroException— tentativa de divisão por zero.
  • IndexOutOfRangeException— índice não apropriado em um array.
  • InvalidCastException— resulta de um operação inválida de conversão forçada(cast) ou uma tentativa de uma conversão explicita.
  • InvalidOperationException— O objeto esta em um estado inválido com relação a chamada do método.
  • NullReferenceException— tentativa de referenciar um objeto null.

Você não esta limitado a usar somente as exceções que a .NET Framework oferece pode também criar e lançar as suas próprias exceções. Para disparar uma exceção você usa a cláusula Throw :

Throw new ArgumentOutOfRangeException

onde :

ArgumentOutOfRangeException - informa o tipo de exceção que esta sendo lançada.

Nesta altura eu já posso lançar a questão :

"Qual a melhor maneira de usar e tratar exceções em uma aplicação VB.NET ?"

Creio que você já está consciente que deve procurar capturar diferentes tipos de exceções e saber o que fazer com cada uma delas em uma aplicação VB.NET. Existirão momentos que você deverá lançar uma exceção avisando o usuário de forma amigável para o problema que esta ocorrendo em outras ocasiões você deverá efetuar um tratamento mas elaborado.

De forma geral existem três formas de abordar o problema de tratamento de exceções :

  1. Você captura todas as exceções

  2. Você passa adiante todas as exceções

  3. Você cria sua própria exceção customizada e mapeia todas as exceções para esta classe.

A melhor forma de tratar o problema é a terceira opção onde você cria uma classe de exceção customizada. Quando você cria um tipo de exceção customizada você ganha controle em todas as propriedades da exceção e pode também incluir propriedades a sua classe customizada.

Nota: A linguagem Java todos os métodos usam a instrução throw para lançar uma exceção. A instrução throw requer um argumento simples: um objeto que seja passível de ser lançado. Em Java objetos passíveis de serem lançados são instâncias de qualquer sub-classe da classe Throwable. Se você tentar lançar um objeto que não seja passível de ser lançado o compilador vai recusar na compilação do programa e indicar um erro. A cláusula throws especifica quais exceções o  método pode lançar. Você usa a instrução throws na assinatura do método informando quais exceções está lançando.

Na plataforma .NET não existe a cláusula throws na assinatura de métodos. Desta forma , você deve documentar explicitamente as exceções que esta lançando para que o usuário saiba. Parece que isto foi feito intencionalmente de forma a que se tenha um tratamento de erros centralizado. A plataforma .NET também não possui as Checked Excpetions que a linguagem Java Possui.

Vejamos um exemplo básico para que você entenda melhor:

Imagine o seguinte trecho de código :

Function getCliente() As Boolean


Try

   Dim pessoa As String = Cliente.buscaCliente("Macoratti")

Catch sqle As SqlException

   Throw New ClienteException("Não foi possível localizar o cliente", sqle)

End Try

End Function

Esta função realiza uma busca pelo cliente Macoratti e lança uma exceção do tipo ClienteException se houver um erro do tipo SqlException.

Percebeu que a exceção ClienteException não é uma exceção padrão do .NET Framework. De onde terá ela surgido ?

A resposta é : a exceção ClienteException  é uma exceção personalizada que foi criada pelo usuário e que possui o seguinte código:

Public Class ClienteException

Inherits Exception
 

   Private minhaException_ As Exception

 

   Public Sub New(ByVal mensagem As String, ByVal excMac As Exception)

       MyBase.New(mensagem, excMac)

       minhaException_ = excMac

   End Sub
 

   Public Function getMinhaException() As Exception

       Return (minhaException_)

   End Function

End Class

A classe de exceção customizada - ClienteException - entende a exceção real lançada como um argumento e transforma a mensagem de nível de sistema em outra mensagem para o nível da aplicação. Além disto você tem a exceção original que casou o erro como parte da nova instância do objeto exceção.(minhaException_)

A classe ClienteException usa dois argumentos : a mensagem (mensagem) que podemos exibir e a exceção real (excMac) que causou a exceção. Assim, para saber a real causa da exceção que disparou ClienteException, basta chamar o método getMinhaException().

Aplicando a estratégia

Vamos agora mostrar que a teoria funciona na prática. Vamos aplicar o conceito a uma pequena aplicação feita no VB2005 que faz acesso a um banco de dados Access - clientes.mdb.

1- Crie um novo projeto no Visual Basic 2005 e inclua uma nova classe chamada DataBaseException.vb com o seguinte código:

Public Class DataBaseException

Inherits Exception


Public Sub New()

End Sub


Public
Sub New(ByVal message As String)

   MyBase.New(message)

End Sub


Public
Sub New(ByVal mensagem As String, ByVal inner As Exception)

   MyBase.New(mensagem, inner)

End Sub


End
Class

 

2- No formulário padrão form1.vb inclua os seguintes controles : Label, TextBox(txtCodigoCliente), Button(btnExibirDados) e ListView(lstaClientes) , conforme figura abaixo:

Agora vamos definir duas classes para tratar as seguintes exceções :

  1. CustomerNotFoundException - lançada quando um cliente não for localizado na base de dados

  2. DataBaseUnavaillableException - lançada quando o banco de dados não pode ser acessado   

Inclua um novo módulo de classe de nome CustomerNotFoundException.vb  com o seguinte código :

Public Class CustomerNotFoundException

                 Inherits DataBaseException

 

Private codigoCliente_ As Long


Public
ReadOnly Property CodigoCliente() As
Long

  Get

    Return CodigoCliente

  End Get

End Property


Public
Sub New(ByVal codigoCliente As Long)

    MyBase.New("Código do cliente não localizado.")

     codigoCliente_ = codigoCliente

End Sub
 

End Class

Agora Inclua um novo módulo de classe de nome DataBaseUnavaillableException.vb com o seguinte código :

Public Class DatabaseUnavailableException
                 Inherits DataBaseException

Public Sub New(ByVal ex As System.Exception)
    MyBase.New("O banco de dados esta indisponível no momento.", ex)
End
Sub

End Class

Perceba que ambas as classes herdam da nossa classe customizada DataBaseException.vb que por sua vez herda de Exception.

Vamos criar também uma classe chamada Cliente cujo objetivo será acessar a base de dados e selecionar os dados dos clientes. Para isto inclua um novo módulo de classe chamado Cliente.vb e insira o seguinte código: :

Imports System.Data.oledb


Public
Class Cliente


Private
Codigo_ As
Integer

Private Shared Nome_ As String

Private Shared Endereco_ As String

Private Shared telefone_ As String


Public
Sub New(ByVal database As String, ByVal CodigoCliente As Long)


Dim
cn As New OleDbConnection

   ConectaBD(database, cn)

   GetDadosCliente(cn, CodigoCliente)

   DesconectaBD(cn)

End Sub


Private
Sub ConectaBD(ByVal database As String, ByRef cn As OleDbConnection)

 

cn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" """" & database & """"
 

Try

   cn.Open()

Catch ex As Exception

   Throw New DatabaseUnavailableException(ex)

End Try
 

End Sub
 

Private Sub DesconectaBD(ByRef cn As OleDbConnection)

cn.Close()

cn.Dispose()

End Sub

Private Sub GetDadosCliente(ByVal cn As OleDbConnection, ByVal codigoCliente As Long)

 

Dim cmd As New OleDbCommand

Dim reader As OleDbDataReader

 

cmd.Connection = cn

cmd.CommandText = "SELECT * FROM tblClientes WHERE codigo = " & codigoCliente

reader = cmd.ExecuteReader

 

If reader.HasRows Then

   reader.Read()

   Codigo_ = reader.Item("Codigo")

   Nome_ = reader.Item("Nome")

   Endereco_ = reader.Item("Endereco")

   telefone_ = reader.Item("Telefone")

Else

   Throw New CustomerNotFoundException(codigoCliente)

End If

End Sub

 

ReadOnly Property codigo()

Get

   Return Codigo_

End Get

End Property
 

ReadOnly Property nome()

 Get

    Return Nome_

End Get

End Property
 

ReadOnly Property endereco()

Get

   Return Endereco_

End Get

End Property

 

ReadOnly Property telefone()

Get

   Return telefone_

End Get

End Property


End
Class

Podemos usar o mesmo tratamento de exceção para conexão como SQL Server (no meu caso o SQL Server Express 2005). Estarei fazendo o acesso a tabela tblClientes do banco de dados Cadastro.mdf  que foi criado clicando com o botão direito do mouse sobre o nome do projeto e a seguir selecionando Add -> New Item , selecionando em seguida a opção SQL DataBase com o nome de Cadastro.mdf.

Vamos criar um novo formulário - form2.vb - e incluir os seguintes controles neste formulário : Button (btnExibirDados) e DataGridView (dgv1), conforme figura abaixo:

Agora no evento Click do botão - Exibir Dados

Private Sub btnExibirDados_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExibirDados.Click
 

Dim connection As SqlConnection = New SqlConnection("Data

Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Cadastro.mdf;Integrated Security=True;User Instance=True"
)


Dim
sql As System.Text.StringBuilder = New System.Text.StringBuilder("SELECT * FROM tblClientes")


Dim
da As SqlDataAdapter = New SqlDataAdapter()

da.TableMappings.Add("Table", "tblClientes")


Try

   Dim cmd As SqlCommand = New SqlCommand(sql.ToString(), connection)

   cmd.CommandType = System.Data.CommandType.Text

   da.SelectCommand = cmd

   Dim ds As DataSet = New DataSet("Clientes")

   da.Fill(ds)

   dgv1.DataSource = ds.Tables(0)

Catch exception As SqlException

   Throw New DataBaseException(exception.Message, exception)

Finally

   If Not connection Is Nothing Then

      connection.Dispose()

   End If

End Try

End Sub

Nota:  A string de conexão com o SQL Server 2005 foi obtida através do assistente de criação de Data Source.

Se testarmos os projetos criados veremos que as classes criadas para tratar as exceções funcionam perfeitamente. Fica a seu cargo melhorá-las.

Aguardo você no próximo artigo VB.NET...

Referências:


José Carlos Macoratti