domingo, 30 de março de 2008

ASP.NET, tratando exceções não tratadas

Quando as exceções não são tratadas e recuperadas internamente em nossa aplicação o ASP.NET ainda fornece maneiras para logarmos, liberar recursos e fazer qualquer outro processamento necessário antes de mostrarmos a exceção para o usuários.

Para isto é possível utilizar dos eventos Page_Error e Application_Error, um programado na página e o segundo no global.asax. Dentro destes eventos podemos então chamar o método Server.GetLastError() para obtermos a exception que está ocasionando o erro e podemos também, se só tivermos que logar ou fazer outra manipulação, chamar o método Server.ClearError(), para que o erro não continue sendo propagado.

Na imagem 1, estamos programando o evento da página para adicionar informações ao trace, que permitirá a análise da requisição.



Imagem 1 - Programando o Page_Error

Para programar o evento Page_Error, somente criamos o método com o nome, parâmetros e retorno acima e este foi chamado devido ao atributo AutoEventWireup da diretiva @Page estar definido para true, conforme Imagem 2.



Imagem 2 - Atributo AutoEventWireup

No global.asax podemos programar o método Application_Error que deve ser utilizado para logar, armazenar informações, enviar notificações e outros procedimentos sobre o erro. Veja na Imagem 3.



Imagem 3 - Application_Error

Na página é possível também configurar o atributo ErrorPage, para definir qual página será executada ao acontecer um erro não tratado.

Configurando o Web.config

No web.config, no elemento customErros, podemos configurar qual a página de erro padrão e página de erros para os diferentes códigos de errors HTTP. O atributo mode do elemento customErrors pode ter os seguintes valores:

  • On
    Exceções não manipuladas redirecionarão o usuário a página configurada no atributo defaultRedirect. Este é utilizado mais frequentemente em produção.
  • Off
    Usuários visualizarão as informações de exceção da página padrão de erro padrão do ASP.NET, esta mostra várias informações sobre o erro. Utilizado frequentemente no desenvolvimento.
  • RemoteOnly
    Usuário acessando a máquina usando localhost na url visualizarão o erro padrão do ASP.NET, enquanto usuários remotos serão redirecionados para a página configurada no atributo defaultRedirect

Veja um exemplo de configuração na Imagem 4

Imagem 4 – customErrors no web.config

Referências:

http://msdn2.microsoft.com/en-us/library/ms954599.aspx

http://msdn2.microsoft.com/en-us/library/ms972393.aspx

http://msdn2.microsoft.com/en-us/library/aa479319.aspx

sexta-feira, 28 de março de 2008

Pensando no resultado esperado

TDD ou Test Driven Develpment é relacionado ao conceito de testar antes de codificar, proposto pela metodologia XP(Extreme Programming). Para quem é averso a sopa de letrinhas ou acha que é modismo, no mínimo vale a pena conhecer a proposta. Eu particularmente gosto da idéia de quando você for codificar, no mínimo já tenha quebrado o problema que vai resolver em pedaços menores e tenha em mente a competência e o escopo de cada parte. Isto faz com que você pense na solução e no resultado esperado pelo usuário, e esqueça as dificuldades de implementação no momento da concepção da idéia.

Este link em português da linha de código dá uma boa introdução para quem tiver dificuldade com o inglês: http://www.linhadecodigo.com.br/Artigo.aspx?id=510

O NUnit é a framework que permite o teste unitário mais utilizada pela comunidade .Net atualmente. Quem vem do java ou ouviu falar do conceito deve estar achando o nome parecido com o JUnit. Não é só o nome que parece, a idéia é a mesma.


Vale a pena ficar de olho também no XUnit.net, que é uma framework de teste unitário que pretende substituir o NUnit, estes blogs em inglês dão uma boa introdução:
http://weblogs.asp.net/rosherove/archive/2007/09/21/xunit-net-aims-to-be-the-next-nunit-but-it-s-too-soon.aspx
http://blog.benhall.me.uk/2008/01/introduction-to-xunit.html

E este é o site do projeto no CodePlex:
http://www.codeplex.com/xunit

No mínimo vale a pena conhecer.

quinta-feira, 27 de março de 2008

Erros acontecem

Mais cedo ou mais tarde nossa aplicação terá um comportamento técnico inesperado, uma situação até então não conhecida, ou simplesmente um bug ou erro. Talvez o pior deles seja achar que não erramos, dessa forma não teremos atuado para contorná-los ou ter coletado informações suficientes para que quando um erro aconteça, seja apenas uma vez e possamos descobrir seu motivo e resolve-lo.

Problemas acontecem durante o processo de desenvolvimento de software e profissionais trabalham no sentido codificar de forma defensiva, logando e manipulando erros inesperados.

Com o tempo e experiência a tendência é desenvolvermos produtos mais maduros, no sentido de ter menos bugs, mas também melhorando nosso processo de manipulação de erros. Pois é assim, por mais que você tenha feito sua parte, o disco rígido pode estar cheio, a conexão pode cair no meio de um processo. Até mesmo um inesperado inseto pode atrapalhar nossa vida...

Exception

As exceções nos programas do .net são tratadas todas de forma uniforme, independente de linguagem. Ou seja, as exceções que ocorrerem em nossas implementações ou em assemblies .net de terceiros, serão tratados da mesma maneira.

Podemos definir exceção como um comportamento ou estado esperado de forma implícita pelo sistema que não é atendido. Um exemplo seria ler um arquivo em disco que não existe, outro exemplo seria programarmos uma exceção para informar um comportamento ou estado inesperado.

As exceções são classes System.Exception ou descendentes desta e a .Net Framework já disponibiliza várias exceções que podem ser utilizadas. É interessante pensarmos no desenvolvimento de nossos sistemas em criarmos exceções específicas do nosso sistema, e estas devem descender de System.ApplicationException. Apesar da Classe ApplicationException descender de Exception, ela não acrescenta nenhum comportamento específico, apenas é uma prática sugerida A imagem 1 demonstra a hierarquia de exceções.


Imagem 1 - Hierarquia de Excecoes.gif

Dentre as propriedades mais utilizadas da classe Exception estão a propriedade Message, que tem as mensagem da exceção e a propriedade InnerException, que pode conter a exceção que ocasionou a exceção atual, isto será exemplificado no próximo parágrafo.

As exceções devem ser detectadas pelo nosso sistema, se a exceção for detectada por um método de nosso código e for possível recuperar desta, ou seja, atuar para resolvê-la, isto deve ser feito. Caso não seja possível a recuperação e tenha sido feito a detecção da exceção, podemos gerar informações do contexto atual, efetuarmos a liberação de recursos utilizados e repassarmos uma exceção para o método chamador. Veja na Imagem 2 este fluxo.

Imagem 2 - Tratamento de uma exceção

Em última instância, ou seja, caso uma exceção não seja manipulada e recuperada em nenhuma parte do programa, podemos logar a exceção, notificar usuários, informar o usuário do erro e devemos também liberar os recursos utilizados. Veja este esquema representado na Imagem 3.

Imagem 3 - Tratando exceções em última instância

Veremos agora como a .Net Framework, o C# e o ASP.NET facilitam a implementação deste tratamento.

Detectando, recuperando, liberando recursos ou repassando exceções

O C# implementa o detectar, recuperar, liberar e repassar exceções através das declarações try-catch-finally e throw. Por causa desta implementação ser um recurso do C# é utilizado para o desenvolvimento de aplicações WebForms ou WinForms da mesma maneira. Para mostrarmos uma aplicação, suponha que precisemos consultar no banco de dado AdventureWorks o total de contatos cadastrados na tabela Person.Contact, veja a imagem 4:

Imagem 4 - Try Finally.jpg

O bloco de código try é executado até o seu final ou até que uma exeção seja levantada. No bloco de código try devemos colocar todo e somente o código que pode levantar a exceção que queremos manipular, ou neste caso, garantir que o recurso seja liberado. Devemos evitar colocar códigos desnecessários no bloco try para liberarmos os recursos assim que não precisemos mais utilizá-los. O bloco finally é sempre executado, independente se o bloco try foi executado com sucesso ou alguma execeção foi levantada.

Uma outra sintaxe para utilizarmos algum recurso garantindo que este seja liberado após sua utilização é a declaração using. A Imagem 5 tem a mesma funcionalidade que o da imagem 4. A declaração using, permite utilizar-se de classes que implementam a interface IDisposable para que este recurso seja fechado após sua utilização.


Imagem 5 - Declaração Using.jpg

Podemos também querer recuperar de um exceção ou adicionar informações de um contexto do método ou parte do código. Vejamos na imagem 6



Imagem 6 - Try Catch Finally.jpg

O bloco catch é executado quando uma exceção é levantada pelo bloco try. Outro ponto importante é que somente o primeiro catch, que é do tipo da exceção levantada ou de algum dos tipos ancestrais da exceção levantada, é executado. Então devemos colocar as exceções mais genéricas por último. Depois da execução do bloco catch o bloco finally é executado se existir.

Caso queria ver mais sobre o tratamento de exceções, confira uma entrevista do Anders Hejlsberg, onde explica porque da opção de não implementar no C# o conceito de Checked e Unchecked exceptions, conceito este, existente no Java: http://www.artima.com/intv/handcuffsP.html


Referências:

http://msdn2.microsoft.com/en-us/library/ms954599.aspx

http://msdn2.microsoft.com/en-us/library/0yd65esw(VS.80).aspx
http://msdn2.microsoft.com/en-us/library/dszsf989(VS.80).aspx
http://msdn2.microsoft.com/en-us/library/dszsf989(VS.80).aspx
http://msdn2.microsoft.com/en-us/library/1ah5wsex(VS.80).aspx
http://weblogs.asp.net/jasonsalas/archive/2005/02/08/368811.aspx