vtortola.NET Logo
Tratando con excepciones

Tratando con excepciones

por vtortola domingo, 09 de diciembre de 2007

Buff... cuanto tiempo desde mi último post, entre estudiar y la medio-gripe que arrastro desde hace unas semanas no tengo ganas tiempo de nada :P

Un tema que siempre me ha parecido que se deja un poco olvidado a la hora de desarrollar una aplicación es el tema de las excepciones, su flujo, su registro y como afectan al diseño y modularidad de la aplicación.

Una excepción, es un error en el flujo lógico ideal de nuestra aplicación causado por una condición inusual. Ante un error de este tipo, debemos actuar para corregirlo y/ó recuperar un estado consistente ó registrarlo en algún lugar(archivo log, EventLog, ..etc..) donde el administrador pueda ver que ha sucedido y tomar medidas. Esto es algo bien sabido por todos, pero el problema viene cuando hay que decidir que hacer con las excepciones que no podemos solucionar y como registrar de forma descriptiva el problema para poder solventarlo.

El objetivo de capturar una excepción, es enfrentarse a una situación inesperada, por lo que debemos capturar las excepciones que podamos solucionar y las que no, dejarlas subir por la pila de llamadas hasta el punto donde se registren como un error no controlado. Por este motivo, esta ampliamente desaconsejado capturar de forma generalizada:

      try
      {
        // lógica...
      }
      catch (Exception)
      {
        // Todas las excepciones son capturadas..
      }

Sin embargo, hay situaciones donde es vital capturar la excepción aún cuando no se puede hacer nada para contrarrestarla, por ejemplo cuando usamos transacciones, es vital capturarla para deshacer las operaciones realizadas. En estos casos, una vez tomadas las acciones oportunas podemos re-lanzarla de nuevo:

      MessageQueueTransaction tran = new MessageQueueTransaction();
      try
      {
        tran.Begin();
 
        // Trabajar en el contexto de la transacción
 
        tran.Commit();
      }
      catch (Exception)
      {
        // Ha habido problemas, realizamos un rollback
        tran.Abort();
 
        // Re-lanzamos la excepción para que sea tratada
        // en capas superiores.
        throw;
      }
      finally
      {
        tran.Dispose();
      }

Antaño (VB6, ASP,..etc...) era necesario capturar la excepción allí donde ocurriese para poder registrar el punto donde sucedió, pero las excepciones de .NET Framework (que derivan todas de la clase Exception) contienen la propiedad StackTrace que registra la consecución de llamadas que han sucedido en la pila hasta toparse con dicha excepción. Sabiendo esto, no necesitamos poner bloques try-catch en cada una de nuestras funciones para saber el lugar exacto donde sucedió.

Cuando se vaya a re-lanzar una excepción es importante hacerlo simplemente con "throw;" ya que preserva la información del StackTraceNO como el siguiente ejemplo, ya que se perdería el  StackTrace original:

      catch (Exception ex)
      {
        // Ha habido problemas, realizamos un rollback
        tran.Abort();
 
        // MAL: Así perdemos el StackTrace original.
        // Sería más difícil encontrar el problema.
        throw ex;
      }

Cuando se quiera usar tipos de excepciones propios ó simplemente otro para dar mensajes de error más descriptivos y/ó completos, podemos usar la propiedad InnerException para poder mantener la información original del error:

catch (ArgumentNullException ae)
{
  throw new ArgumentException("El objeto no era válido", ae);
}

De esta forma, se mantiene la información del error original.

No se debe utilizar las excepciones como elementos de nuestra lógica y debemos evadirlas en la medida de lo posible ya que el uso de excepciones implica una penalización de rendimiento. Cuando digo evadirlas, me refiero a que si esperamos un error muy concreto... mejor procurar que el error no suceda, por ejemplo:

static Int32 Divide(Int32 a, Int32 b)
{
  Int32 result = 0;
  try
  {
    result = a / b;
  }
  catch (DivideByZeroException)
  {
    result = -1;
  }
  return result;
}

En este sencillo ejemplo, contemplamos la posibilidad de que la variable 'b' sea 0 y una excepción de tipo DivideByZeroException ocurra. Aunque se toman medidas para devolver un posible valor inocuo, se podría evitar fácilmente:

static Int32 Divide(Int32 a, Int32 b)
{
  Int32 result = 0;
  if (b == 0) return -1;
  result = a / b;
  return result;
}

El como registrar las excepciones no controladas es algo ya más dependiente de los requerimientos del proyecto ó conveniencia técnica, se puede usar desde un simple archivo de texto, el EventLog ó por ejemplo el Logging Application Block de Enterprise Library 3.1 (del que espero escribir en breve... si no las pincho antes xD).

Como esto se hace largo ya... en un próximo post escribiré algo sobre donde y porque capturar excepciones de forma que no afecte a la modularidad de nuestra aplicación.

Links:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET 2.0 | C# 2.0

Related posts

Comentarios

diciembre 9. 2007 14:24

trackback

Trackback from Pensando en asíncrono

Tratando con excepciones

Pensando en asíncrono

diciembre 9. 2007 14:26

trackback

Trackback from vtortola

Tratando con excepciones

vtortola

diciembre 10. 2007 08:46

Gravatar

En realidad el comentario es por tocar un poco las narices, y por ver si al menos así hay algún enlace externo a mi blog (sí, ya lo sé. es patético)

Cuando usamos un bloque try..catch para controlar una transacción, en el ejemplo que has puesto, lo suyo es usar un catch a secas, sin el Exception ex, ya que si no haces nada con la variable, no tiene sentido instanciarla. Además, el compilador te mostrará un warning.

La cosa quedaría así:
try
{
// Configurar command
// ...
dc.Transaction = trans;
// Ejecutar
dc.ExecuteNonQuery();
// Commit
trans.Commit();
}<B>
catch
{
// RollBack
trans.Rollback();
throw;
}</B>

Puedes ver el ejemplo completo en:
http://cs.crisfervil.com/blogs/crisfervil/archive/2007/11/28/70-528-resumen-ado-net-y-xml.aspx

Salu2


Cristhian es

diciembre 10. 2007 11:46

Gravatar

Si y no :D

Si lees bien el ejemplo de la transacción, no he puesto "catch(Exception ex)" sino "catch(Exception)", esto no crea una instancia, simplemente define un filtro. Pero aún así, "catch(Exception ex)" tampoco crea una instancia, la instacia se crea en el mismo momento que la excepción se produce, el hacer "catch(Exception ex)" solo define una variable para poder acceder al objeto Exception que se ha creado a raiz del error. Uses una cosa u otra la instancia existe igual ;)

La diferencia entre "catch(Exception)" y "catch" a secas es que el primero solo "captura" las excepciones administradas mientras que el segundo captura las administradas y las no administradas (non-CLS), es decir, "catch" a secas se lo come todo :D es un método para filtrar. En un entorno full-managed no tiene sentido definir un filtro tan amplio... bajo mi gusto claro, aunque si en la transación juegan elementos no administrados como COM+ en un MSDTC, habría que indicarlo como tu dices, con un "catch" pelado.

Evidentemente el código era para mostrar el ejemplo :)

Un saludo.

vtortola es

diciembre 13. 2007 12:22

Gravatar

Hola,
se podría poner algo así:

catch (Exception ex)
{
// para administrada
}
catch
{
// para no administradas
}

saludos.

espinete

enero 9. 2008 10:06

Gravatar

Yo no entiendo muy bien todo esto de lo que hablais, estudie algo, pero vais a un nivel superior no?

casas

Comments are closed

Powered by BlogEngine.NET 1.1.1.8
This theme is a variation of Mads Kristensen by Valeriano Tórtola

Valeriano Tórtola

Personal Ver perfil
E-mail Enviar correo
LinkedIn LinkedIn
Fotos Fotos
MCPD

Publicidad

Posts recientes

Disclaimer

Las opiniones mostradas aqui son mis opniones y no representan el punto de vista de mi empresa en ninguna forma.

Creative Commons License

Esta obra está bajo una licencia de Creative Commons

Locations of visitors to this page

© Copyright 2009

Sign in

Calendario

<<  julio 2009  >>
lumamijuvido
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

Ver en calendario extendido