enero 23, 2006

Sobre la administración de excepciones (2)

¿Sabías que puedes monitorear la cantidad de excepciones que tu aplicación está lanzando? Se puede hacer de dos maneras sencillas:

- Monitoreando periódicamente el log de excepciones, siempre que se haya implementado código para manejo de excepciones (similar a lo indicado en la entrada anterior).
- Con el Performance Monitor de Windows, se puede ver el valor del contador # of Exceps Thrown / sec que se encuentra en el objeto .NET CLR Exceptions. Digamos que para indicar un manejo adecuado, este valor deberá ser menor que el 5% de tus solicitudes por segundo.

Otra manera de evitar que las excepciones afecten al rendimiento de una aplicación, es hacerse cargo de aquellos recursos que son tomados y que al ocurrir una excepción podrían eventualmente quedar en memoria. Para esto pues utilizamos la cláusula finally. Con esto no aseguraremos que los recursos son liberados una vez ocurrida la excepción. De la siguiente manera:

try
{
conn.Open();

}
finally
{
If (null !=conn) conn.Close();
}

Y lo más importante, escribir código que evite las excepciones. Por ejemplo.

Verificar valores nulos. En caso de ser posible que un objeto tenga el valor null, habría que asegurarse que no es nulo, en lugar de lanzar una excepción. Esto comúnmente sucede cuando se tratan de consultar ítems del session state, view state, application state, u objetos del cache. Por ejemplo, NO conviene utilizar el siguiente código para acceder información a un objeto de sesión:

try{
loginid = Session[“loginid”].ToString();
}
catch (Exception ex){
Response.Redirect(“login.aspx”,false);
}


En su lugar, bien podríamos utilizar

if (Session[“loginid”]!=null)
loginid = Session[“loginid”].ToString();
else
Response.Redirect(“login.aspx”,false);


No utilizar las excepciones para controlar lógica. Las excepciones son eso – excepciones. Una falla en la conexión a una base de datos es una excepción. Un usuario que se equivoca en digitar el password es una mera condición que necesita ser manejada. Por ejemplo, el siguiente código:

public void Login(string UserName, string Password) {}

El siguiente código se utiliza para llamar al login.

try
{
Login(userName, password);
}
catch (InvalidUserNameException ex)
{…}
catch (InvalidPasswordException ex)
{…}


Es mucho mejor crear un enumerado de los posibles valores y cambiar el método login para que retorne la enumeración; algo así:

public enum LoginResult
{
Success, InvalidUserName, InvalidPassword, AccountLockedOut
}
public LoginResult Login(string UserName, string Password) {}


Y utilizar el siguiente código para llamar a Login:

LoginResult result = Login(userName, password);
switch(result)
{
case Success: …
case InvalidUserName: …
case InvalidPassword: …
}


Suprimir las llamadas internas a Response.End. Los métodos Server.Transfer, Response.Redirect y Response.End, levantan excepciones. Cada uno de ellos llama internamente al método Response.End. La llamada a Response.End, provoca una excepción ThreadAbortException. Si utilizas Response.Redirect, mejor utiliza el método recargado y pasa false al segundo parámetro para evitar la llamada interna a Response.End. Más información en: PRB: ThreadAbortException Occurs If You Use Response.End, Response.Redirect, or Server.Transfer.

No atrapar excepciones que no estamos manejando. Si el código no puede manejar una excepción, utiliza un bloque try/finally para asegurar que se cierran los recursos, aún si la excepción ocurre o no. No atrapes la excepción si no se intenta recuperar; antes bien, permítele propagarse para un manejador adecuado que pueda “lidiar” con la excepción encontrada.