Buenas Prácticas en Manejo de Excepciones .Net - WarNov Developer Evangelist - Site Home - MSDN Blogs

Buenas Prácticas en Manejo de Excepciones .Net

Buenas Prácticas en Manejo de Excepciones .Net

Rate This
  • Comments 18

Hace poco me preguntaron acerca de cómo se debería de abordar el manejo de excepciones para una aplicación grande.

Me sentí agradado de que esa persona estuviera concientizada de que no es un tema que se puede dejar al azar y que hay que tener ciertos temas en cuenta.

Básicamente, su duda era acerca de cómo capturar unificadamente todas las excepciones producidas en su aplicación; luego de alguna investigación, llegó a saber de Application.ThreadException; un evento al que se le puede poner un manejador y allí tomar las acciones necesarias. Sin embargo, personalmente no recomiendo esta solución. Por qué?

1. La notificación de la excepción ocurre muy tarde: cuando se recibe la notificación, la aplicación ya no podrá responder a la excepción.

2. La aplicación terminará abruptamente si la excepción ocurre en el hilo principal o en cualquier otro hilo que sea iniciado por código no administrado (el hilo principal de la aplicación obviamente es ejecutado por Windows – no administrado)

3. No tenemos acceso a ninguna información valiosa acerca del error, excepto la excepción misma. No se podrán cerrar conexiones a bases de datos, hacer rollback de transacciones ni nada útil. 

Así que mi recomendación es nunca basar el manejo de excepciones en los eventos Application.ThreadException ni AppDomain.UnhandledException. Ellos deben ser usados y existen como las redes de seguridad de los acróbatas; su función es la de hacer un registro de la excepción en algún mecanismo de log, para alguna examinación futura. Así que hay que darse la pela y manejar cada excepción en su sitio y actuar en concordancia.

Esta última invitación conlleva a tener en cuenta otra serie de factores que generalmente se omiten cuando uno comienza a desarrollar “en forma”:

1. El código metido entre try, catch se ejecuta muy lento. Consume muchos recursos. Así que sólo manejemos excepciones cuando realmente se necesite. Cuándo es eso?
Las excepciones están construidas para manejar condiciones que no se pueden controlar con la lógica de la aplicación. Por ejemplo que se cae la conexión con el servidor, o que el disco está lleno, o que el hardware falló…
Antes de lanzar excepciones por todo, fijémonos si realmente es necesario. Evitemos también poner mucho código en el try; solo afectemos con el try el código que realmente puede fallar. Por ejemplo es preferible:

if (conn.State != ConnectionState.Closed)
{      
   conn.Close(); 
} 

a:

try
{
        conn.Close(); 
} 
catch (InvalidOperationException ex)
{
     Console.WriteLine(ex.GetType().FullName);
     Console.WriteLine(ex.Message);
} 

2. No emita Exception(). O es que eso le da mucha información?
El framework .net tiene un número de excepciones que nos permite controlar argumentos y operaciones inválidas, timeouts, conexiones, overflows, etc. Usemos esas excepciones que realmente nos indican qué fue lo que sucedió.

3. Use bloques try/finally. Recuerde que las instrucciones en el Finally siempre se ejecutan independientemente de si se produjo o no la excepción. Esto es muy útil para liberar recursos, cerrar conexiones, etc.

4. Siempre use un catch para cada tipo de excepción que se pueda generar y ordénelos desde el más específico hasta el menos específico. Esto ayuda a identificar plenamente el error que se generó y no terminar con excepciones como: “Object reference not set to a instance of an Object”.

5. Cuando cree excepciones propias del aplicativo, asegúrese de distribuir el Assembly a una ubicación compartida para todos los posibles dominios que tengan que ver con ellas.

6. De acuerdo a como el mecanismo de excepciones funciona, es muy importante siempre que creemos excepciones propias, proveerlas al menos con los tres constructores de excepciones básicos.

7. Siempre es recomendable usar las excepciones de .Net. Las personalizadas solo tendrán que ver con escenarios programáticos.

8. Siempre se ha pensado que para las excepciones personalizadas se debería heredar de ApplicationException; sin embargo en la práctica se ha demostrado que no se agrega valor significativo y además se pierde cierto performance. Así que es mejor heredar siempre de Exception.

9. Use mensajes gramáticamente correctos.

10. Las excepciones pueden incluir propiedades. Estas propiedades pueden ser accedidas programáticamente para tomar acciones. Incluya información extra relevante en estas propiedades cuando sea útil.

11. En vez de retornar null, lance excepciones en la mayoría de casos. Eso evita inconvenientes que a veces son difíciles de detectar. Evite también retornar códigos de error.

Estas son solo unas pocas recomendaciones a la hora de manejar excepciones. Hay muchas más que se aprenden con el pasar de las líneas de código.

Para finalizar, me parece importante mencionar también que igual existe un Application Block en Patterns And Practices para el manejo de Excepciones. Incluyéndolo en nuestras aplicaciones ya de por sí garantizaremos varias buenas prácticas; eso sí, usandolo tal y cual se explica en los lineamientos.

Leave a Comment
  • Please add 6 and 3 and type the answer here:
  • Post
  • excelente articulo!!!!

  • Muy buen post Walter... la verdad siempre he intentado seguir estas reglas y la verdad uno se aconstumbra con el paso del tiempo.. y como dices, es mejor validar antes de usar try - catch...

  • Hola walter,

    Tengo una duda con el Exception Handling Application Block Hands-On Labs for Enterprise Library, para ser mas preciso si intenta ejecutar el Laboratorio Final del ejercicio 1 y 2 arroja errores, como si el laboratorio no funcionara, para el caso del ejercicio 1 cuando se intenta lanzar la excepción por medio de la clase Throw produce un error ya que no encuentra el manejador del evento, y en el ejercicio 2 pasa igual solo que esta vez con las directivas de seguridad, quisiera saber en donde esta el error ya que se supone que el proyecto en la version final deberia funcionar sin problemas.

    Estoy ejecutando el proyecto con VST2010 Framework 3.5 y 4.0,

    De antemano muchas gracias por su tiempo.

  • Don walter, muy bueno el articulo y muy inclusive para mi, solo una cosa, he visto que es muy comun usar codigos de error, ¿por que no se recomienda? ¿Que estrategia seria la adecuada?

    Muchas gracias y muy buen articulo

  • Muchas Gracias Oscardo.

  • Así es Ricardo, Jorge-...  Digo, Julio!

    La práctica hace al maestro ;)

  • Hola Joseph. Estuve probando los HOL en una máquina y me funcionaron correctamente.

    Me puedes por favor mostrar el error que te sale?

  • @Odahir!

    Viejo Odahir.... que bueno tenerle por aquí...

    Cuando uno bota códigos de error, automáticamente se embarca en la tarea de generar lo mecanismos para administrar esos códigos.

    Creación de nuevos códigos para nuevas excepciones, enseñar las tablas a los clientes de la aplicación, sincronizar dichos códigos cuando cambian... y todos los problemas que ello puede generar. Por ejemplo que un cliente tenga la v1.0 de la tabla de códigos de error, y al acceder a ala v2.0 hagan falta códigos, generándose entonces una excepción sobre excepciones...

    Los códigos de error fueron una gran alternativa en ambientes donde las excepciones no existían (VB, C, T-SQL)

    Pero en un lenguaje administrado como C# o VBNet, el mismo framework ofrece un conjunto de de excepciones que representan exactamente lo que sucedió (aquí se pueden ver algunas: www.developerfusion.com/.../3) y a las que además podemos enriquecer agregándoles datos inherentes a la excepción ocurrida; sin mencionar que por ejemplo también tenemos información del stacktrace, de manera que los clientes siempre se pueden enterar exactamente de qué fue lo que pasó sin necesidad de recurrir a tablas de equivalencia que a veces no tienen o están desactualizadas.

  • Algunos de los lineamientos que uso a la hora de pensar en excepciones son:

    - En cuanto a lanzar excepciones, hay que pensar que las excepciones son excepcionales, por lo tanto, solo deberían lanzarse cuando no se puede cumplir el propósito del método (que debería estar expresado en el mismo nombre del método) por alguna situación.

    - Respecto a capturar y manejar una excepción, normalmente debería dejarse esto a "la capa de presentación", preferiblemente mediante un manejador centralizado. Solo tendría sentido atrapar y manejar la excepción en niveles inferiores si realmente se puede hacer algo allí para solucionar la situación o al menos agregar más detalles para que sea más entendible o algo así.

  • @Jorge Gamba.

    Respescto al primer pnto creo que no se hubiese podido haber dicho mejor.

    Pero en el segundo difiero, por cuanto si uno no da cierto manejo en el negocio de la aplicación a sus excepciones, es muy frecuente (y lo digo porque me he encontrado muchas aplicaciones así), que la pobre capa de presentación e encuentre muy a menudo con excepciones del tipo:

    "Object Reference not Set to an instance of and Object" ante las cuales ya no hay mucho que hacer o que saber.

    Por otro lado por ejemplo en ambientes transaccionales, dejar pasar la excepción a través de todas las capas hasta que vuelva a la de presentación sin hacer ningun manejo, puede resultar en consecuencias algo desastrozas; por ejemplo cuando el cliente que representa la capa de presentación no tiene una comunicación directa con el server (por ejemplo un cliente java comunicándose a través de soap con un servidor .net)

    Y finalmente reitero que un manejo de excepciones centralizado usando por ejemplo Application.ThreadException o el evento OnError del Global.asax implica dejar que la excepción llegue a su última instancia cuando poco o nada queda por hacer. Lo único que debería generarse de esta manera, es un proceso de logging de errores por ejemplo, obviamente habiendo suministrado información adicional a la excepción en las capas más internas de la solución. Si no, el mensaje registrado en el log, tampoco será de mucha utilidad.

  • @Warnov

    Tal vez debí ser más detallado, mencioné "la capa de presentación" entre comillas porque precísamente quería hacer referencia al escenario típico de una aplicación en .NET, pero digamos que quize decir la capa de más alto nivel bajo nuestro control, lo importante es tener un manejo centralizado de las excepciones sobre las que no se puede hacer nada por "realmente solucionar" el problema (no simplemente mandarlo al log) o al menos mejorar la información que provee (ej. agregando más detalles o cambiando la excepción por una más específica).

    Cuando digo que deben subir estas excepciones a un nivel más alto, incluso a la presentación, no me refiero a que sean mostradas al usuario tal cual, porque estas son realmente errores que no son responsabilidad del usuario de la aplicación final, sino del desarrollador que no previó y controló alguna situación o problemas de infraestructura física, por ejemplo. Nuevamente, me refiero es a que se les dé un manejo centralizado y no disperso, en cada lugar en donde se pueden producir.

    Por otro lado, idealmente uno no debería manejar excepciones en el negocio, si no aplican las excepciones que he comentado antes (que uno pueda solucionar o mejorar la excepción) porque precísamente se trata del "negocio" y deberíamos hacer todo lo posible por no contaminarlo, por ejemplo, la excepción que pones como ejemplo ("Object Reference not Set to an instance of and Object") no tiene nada que ver con una clase para realizar asientos contables, entonces aún si se produce en esta clase, no deberíamos ponerle la responsabilidad de enviar a un log o algo así.

  • Para continuar con el mismo ejemplo, ¿cómo manejarías la excepción "Object Reference not Set to an instance of and Object"? si ocurre digamos en un método 'AsentarRegistro' en una clase AsientosServicio o algo así, en una app sencillita y clásica de 3 capas en ASP.NET.

  • Estuve leyendo muy atentamente tu entrada y esta bastante interesante, pero me quedo la duda respecto al performance de bloques de codigo, dentro de try catch, mira encontre esto www.chinhdo.com/.../try-catch-blocks-performance ò www.programmersheaven.com/.../175-Do-trycatch-blocks-hurt-runtime-performance donde llegan a la conclusion de que el costo de agrupar codigo en bloques try catch es casi nulo, no se que pruebas has realizado para decir que  "El código metido entre try, catch se ejecuta muy lento. Consume muchos recursos".

    Gracias por acalrarme esta duda

    Eduardo Ballesteros

  • @Eduardeno Ballesteros

    Saludos Eduardo:

    Te agradezco, pues tu inquietud me ha dado para poner otro postg que explica las razones de mis afirmaciones acerca de los bloques try catch.

    Encuentralo aquí:  blogs.msdn.com/.../por-qu-233-los-bloques-try-catch-s-237-afectan-el-performance.aspx

  • Amigo muchas gracias por sacarme de la duda, pues siempre acostumbro a meter la mayor parte de mi codigo dentro de bloques try, catch.

    Gracias

Page 1 of 2 (18 items) 12