sábado, 6 de enero de 2007

Acerca del manejo de errores en .net entre capas

Debemos de decidir donde manejar los errores, para tomar una estrategia y diseño para el mismo. Podemos escoger entre enviar un correo, grabar un archivo log, etc. Este comentario no pretende a profundidad ver el tema sino colocar una idea en nuestras mentes de lo que podemos hacer. Nivel: Medio.

El manejo de errores es algo que no nos preocupa mucho especialmente si trabajamos en una empresa a la cual desarrllamos, cuentan con nosotros y si hay un error, pues lo resolvemos. Pero no deberiamo pensar así. Por lo menos debemos de darle la impresión al usuario que tenemos controlada la situación, y reducir la interrupción del programa por un error no manejado.

Por un tiempo creí que necesitama arrastrar los errores entre capa y capa. Hasta que leí un articulo sobre las mejores practicas de manejo de errores, y allí expliacaban que no era necesario y que era costoso estar tirando los errores si hacer nada de ellos de la forma tradicional que conocemos.

En c#:
try
{
/// Codigo suceptible a factores variables y externos que pueden sufrir el error.
}
catch(Exception ex)
{
throw new Exception(ex.Message);
}


Esto lo hacia en cada capa: Datos, Logica de negocio, UI. Pero no era necesario. Por el momento por el tiempo a invertir y no sentarme a definir la estrategia de manejo de errores. Elimine de las capas el famomo try catch y lo deje en la UI, para mostrar un mensaje de error al usuario y no interrumpir el funcionamiento del mismo. Esto redujo el overhead que se produce en el atrapar el error y volvelo a tirar sin hacer nada mes, esto no es necesario en la capa de presentación o UI, allí atrapamos el error y mostrar un mensaje al usuario y podemos enviar un correo. Esto lo veremos mas adelante en otra publicacion del blog.

Lo que si se recomienda es que se utilice el try - finally en especial en la capa de acceso a datos DAL. Para garantizar que si hubo un error del lado de la base de datos o del codigo que parsea los datos hacia nuesta coleccion de datos, dataset, o entidad de negocio se cirre la conexion abierta.

Aquí un ejemplo utilizando Microsoft Enterprise Library 2.0 en c# 2.0. :

public List<PeriodoMensual> ProximoPeriodo( PeriodoMensual parametro)

{

List<PeriodoMensual> Resultado = null ;

IDataReader idr = null;

try

{

DBCommand dbcw = base.GetStoredProcCommandr(SPListar);

Parser.ParametrosSoloLlavePrimaria<PeriodoMensual>( ref dbcw, parametro);

idr =

base.ExecuteReader(dbcw);

Resultado =

Parser.Query<PeriodoMensual>(idr);

idr.Close();

}

finally

{

if (idr != null && !idr.IsClosed) idr.Close();

}

return Resultado;

}


Aquí hay otros elementos que he desarrollado como son los parser para trasladar la información del query resultante a una colección tipo generics de la entidad del negocio, pero lo que es necesario sobre saltar aquí por el tema es el uso del try - finally para garantizar que la conexión se cerro en este caso por utilizar Microsoft Enterprise Library se encargar que cuando mando a cerrar la interface IDataReader esta cierra la conexión.
Por ultimo quisiera comentar que una buena estrategia del manejo de los errores sería que el la capa de negocio de datos atrapar el error con try - catch enviar un correo, guardar el error en una base de datos para estadisticas de errores. Y manejar resultados nulos para que en la ui, solo tenga que prenguntar que si no es nulo el valor o el resultado es verdadero pueda continuar con el flujo del programa en la capa de presentación.

--
Hasta pronto nos vemos luego con mas de desarrollo en .NET con C#,

Manolo Herrera

4 comentarios:

martinyasse dijo...

Muy bueno en texto, me ha servido mucho. Estoy por diseñar un control de errores para mi proyecto y ahora me encuentro mas orientado. Gracias.-

ienogue dijo...

También me ha servido para aclarar un poco el panorama. Quisiera preguntarte, Manolo, si te acuerdas, cuál es el artículo que mencionas que leíste, que te aclaró la forma de manejar excepciones en una arquitectura de capas, y publiques la url. Desde ya muchas gracias

Unknown dijo...

Bueno, si era más que necesario un rediseño en tu lógica para el manejo de excepciones, ya que el código que publicas a manera de ejemplo de lo que hacías antes también está mal implementado:

try
{
/// Codigo suceptible a factores variables y externos que pueden sufrir el error.
}
catch(Exception ex)
{
throw new Exception(ex.Message);
}


Pues atrapas la excepción y generas una nueva, cuando lo correcto hubiera sido:

try
{
/// Codigo suceptible a factores variables y externos que pueden sufrir el error.
}
catch(Exception ex)
{
throw;
}

Con esto, el error original se transportaría al siguiente manejador de excepciones.

Bueno, solo pretendía dejar más clara tu idea, espero que te sirva.

Saludos.

Unknown dijo...

Excelentes observaciones.
También me estado documentando algo acerca del tema de manejo de errores. Es todo un mundo, y parte importante del diseño e implementación de estándares. Y solo como un punto de vista muy personal y en base a la experiencia. Los usuarios se asustan cuando ven pantallas generadas por un throw. Aun que suelen ser útiles para rastrear y solucionar el error. Una forma “elegante” que encontré para que no se muestren esas pantallas y regrese información útil, además que simula control en el sistema y da confianza a los usuarios es esta:
try
{
/// Codigo suceptible a factores variables y externos que pueden sufrir el error.
}
catch(Exception ex)
{
Label1.Text = “Lo sentimos ocurrio un problema en el sistema. Envie esta pantalla a nuestros ingenieros informando que se ha generado el error: ”+ex.Message.ToString();
Label2.Text = ex.Source.ToString();
Label3.Text = ex.StackTrace.ToString();
Label4.Text = ex.TargetSite.ToString();
//como idea muy simple
}

Suena algo raro pero funcionó de maravilla :P
Saludos