.NET - Organizando o tratamento de erros - Nível de método


Se você esta 'mais perdido do que cachorro em dia de mudança' quando a questão é o tratamento de erros em sua aplicação este artigo pode ser a sua tábua de salvação (ou perdição).

Vou começar com o tratamento de erros no nível de métodos e mostrar uma forma (existem muitas outras formas) de organizar as coisas quando os erros ocorrerem em sua aplicação.

Vou usar os recursos e vantagens do tratamento estruturado de exceções da plataforma .net , portanto , esqueça as cláusulas On Error goto/Resume do Visual Basic 6 pois vamos usar a estrutura try/catch/finally.

A estratégia adotada para tratamento de erros esta resumida a seguir de acordo com o cenário :

1- Erros potenciais estão previstos e sendo tratados em uma rotina

Solução - Usar uma combinação de blocos try…Catch como mecanismo de retorno para tratamento de erros;

2- Uma informação importante deve ser incluída na ocorrência da exceção

Solução - Criar e lançar(throw) um nova exceção com a informação anexada;

3- As variáveis/objetos/recursos usados na rotina devem ser liberados da memória

Solução - Realizar a operação de limpeza em um bloco finally

4- Erros potenciais não estão sendo tratados em uma rotina

Solução - A recuperação deve ser tratada pela rotina que fez a chamada no seu tratamento de erros

A estrutura de tratamento de erros da plataforma .NET pode ser usada de diversas formas.

Vejamos a sintaxe básica do bloco Try/Catch para VB.NET e C#:

Private Sub Rotina( )

    Try
          'Codigo a ser tratado
    Catch exc As Exception
          'tratamento de erro
     Finally
           'operação de limpeza
     End Try

End Sub 
      private void Rotina( )
      {
	try
	{
	    // Codigo a ser tratado
	}
	catch (Exception exc)
	{
	    // tratamento de erro
	}
	finally
	{
	      // operação de limpeza
	}
     } 
VB .NET C#

O bloco Try inclui o código que implementa o método.

O bloco Catch é opcional, e inclui o código que trata erros específicos que são identificados e recuperados quando possível.

O bloco finally é opcional,e realiza a operação de limpeza requerida antes que o método encerre devido a um erro ou não. Geralmente as operações referem-se ao fechamento de conexões com banco de dados , liberação de objetos criados no método, etc. O bloco finally sempre será executado quer ocorra ou não uma exceção.

Os blocos Catch e finally são opcionais mas você deve usar um deles obrigatoriamente.

De forma geral você deve usar a estrutura de tratamento de erros em qualquer método um erro possa ocorrer mas a técnica exata depende da circunstância. Veja abaixo um resumo dos possíveis cenários:

NãoN/A
Erros podem ocorrer ? São Recuperáveis? Uma informação importante deve ser anexada ? A limpeza é necessária ? Combinação recomendada de blocos try, catch, e finally
Não N/A N/A Sim TRy e finally somente
Sim Não Não Não Nenhum
Sim Não Não Sim TRy e finally somente
Sim Não Sim Não try e catch somente
Sim Não Sim Sim try, catch, e finally
Sim Sim N/A N/A try e catch only

Nota: Lembre-se que o .NET Framework não fecha as conexões de banco de dados , arquivos ,etc quando erros ocorrem. Esta responsabilidade é do programados (ou seja SUA), e, você deve fazer isso no bloco finally pois esta é a última oportunidade de realizar as ações de limpeza antes que a infraestrutura do tratamento de exceção tome o controle da aplicação.

Para ajudá-lo a definir melhor a implementação de uma estratégia de tratamento de erros temos a seguir algumas questões cujas respostas podem ajudá-lo a tomar esta decisão:

1- Erros podem ocorrer durante a execução do código de uma rotina/método ?

Se a resposta for SIM, então implemente o tratamento usando o bloco try/catch.

2- Erros podem ser recuperados durante a execução do código de uma rotina/método ?

Se um erro ocorre e nada pode ser feito durante a execução do código, você deve propagar a exceção a rotina que fez a chamada da rotina/método onde o erro ocorreu.

Por exemplo se uma rotina tenta escrever um registro em uma tabela de um banco de dados e encontra o registro bloqueado você pode tentar efetuar a gravação mais uma vez (este erro é recuperável).

Se um valor é passado para a rotina e durante a execução da operação ocorre um erro de estouro de pilha a recuperação não é possível na rotina mas pode ser tratada pela rotina que fez a chamada do método no seu tratamento de erros.

3- Uma informação importante pode ser anexada a exceção ?

Exceções que ocorrem na plataforma .NET contém informação detalhada relacionada ao erro; mas as exceções não fornecem qualquer informação de contexto sobre o que esta sendo feito a nível de aplicação no tratamento do erro ou no fornecimento de informação importante ao usuário.

Neste caso uma nova exceção pode ser criada e lançada com a informação necessária. O primeiro parâmetro para a nova exceção deverá conter a mensagem do contexto e o segundo deverá ser a exceção original. O mecanismo de tratamento de exceção da plataforma .NET cria uma linked list de objetos Exception de forma a criar um rastro a partir da exceção raiz até o nível onde a exceção foi tratada. Exemplo:

Catch ex As Exception
       Throw New Exception("mensagem do contexto", ex)   
catch (Exception ex) 
{ 	
   throw (new Exception("mensagem do contexto", ex)); 
} 
VB.NET C#

Um bloco Catch não deve ser usado no fluxo normal da sua aplicação.

O fluxo normal do programa deve ser colocado somente no bloco try e o fluxo anormal deve ser colocado no bloco Catch.

O mecanismo de tratamento de exceções da plataforma .NET é eficiente e poderoso; eu tratei neste artigo das exceções que ocorrem a nível de método/rotina mas outras exceções específicas podem ser capturadas e processadas diferentemente.

Você pode criar uma classe para tratamento de exceções que herda da classe base Exception e pode incluir funcionalidades requeridas pela sua aplicação nesta classe. Um exemplo desta técnica pode ser vista no código a seguir onde uma nova tentativa de atualização é feita após ocorrer o erro:

Imports System
Imports System.Configuration
Imports System.Data
Imports System.Data.OleDb
	Private Sub atualizaDados(ByVal problemID As Integer,  ByVal sectionHeading As String)
		Const MAX_TENTATIVAS As Integer = 5

		Dim dbConn As OleDbConnection = Nothing
		Dim dCmd As OleDbCommand = Nothing
		Dim strConnection As String
		Dim cmdText As String
		Dim updateOK As Boolean
		Dim rContador As Integer
		
		Try
			'obtem a string e conexão do arquivo web.config e abre a conexão
			strConnection = ConfigurationManager.ConnectionStrings("dbConnectionString").ConnectionString
			dbConn = New OleDbConnection(strConnection)
			dbConn.Open( )

			'cria um comando SQL update para atualizar o registro no banco de dados
			cmdText = "UPDATE EditProblem " & "SET SectionHeading='" & sectionHeading & "' " &_
             				  "WHERE EditProblemID=" &problemID.ToString( )

			dCmd = New OleDbCommand(cmdText, dbConn)

			'fornece um laço com o bloco try/catch para facilitar a tentativa de atualizar o banco de dados
			updateOK = False
			rContador = 0

			While ((Not updateOK) And (rContador < MAX_TENTATIVAS ))
				Try
     				    dCmd.ExecuteNonQuery( )
       				    updateOK = True
				Catch ex As Exception
      				    rContador += 1
	      			   If (rContador >= MAX_TENTATIVAS) Then
		       	   	   'lança uma nova exceção com a mensagem de contexto informando 
			          	    'o número máximo de tentativas que foram realizadas
				        Throw New Exception("Número máximo de tentativas alcançada", ex)
				End If
			         End Try
		          End While
	           Finally
	  	  'realiza a operação de limpeza
	  	   If (Not IsNothing(dbConn)) Then
			dbConn.Close( )
		    End If
	           End Try
End Sub 
Abaixo temos um exemplo de uma classe que efetua o tratamento específico com mensagem de erros mais amigáveis:
Option Explicit On
Option Strict On

	''' <summary>
	''' Esta classe fornece uma classe de exceção com suporte a mensagem amigáveis
	''' </summary>
	  Public Class ExcecaoAmigavelVB
		Inherits System.ApplicationException

		'define variável para copiar a mensasgem amigavel
		Private mMensagemAmigavel As String = ""
		'''***********************************************************************
		''' <summary>
		''' Fornece o acesso a mensagem a ser exibida de forma amigável
		''' message.
		''' </summary>
		Public Property MensagemAmigavel ( ) As String
			Get
				Return (mMensagemAmigavel )
			End Get

			Set(ByVal Value As String)
				mMensagemAmigavel = Value
			End Set
		End Property 

		'''***********************************************************************
		''' <summary>
		''' Fornece um construtor com suporte a mensagem de erro, uma referencia a
		''' a exceção que lançou esta exceção e uma mensagem amigável.
		''' </summary>
		Public Sub New(ByVal message As String, ByVal inner As Exception, _
					  ByVal MensagemAmigavel As String)
			'chama o construtor da classe base
			MyBase.New(message, inner)
			mMenagemAmigavel = MensagemAmigavel 
		End Sub 'New
	End Class 

Com esses conselhos espero ter contribuído para auxiliá-lo no gerenciamento do tratamento de erros da sua aplicação. Aguarde em breve um artigo sobre tratamento de erros em páginas ASP .NET.

Referências:


José Carlos Macoratti