Exception Handling Application Block

Exception Handling Application Block ayuda a simplificar y estructurar el manejo de excepciones en una aplicación.
El código que va en la clausula catch delega el manejo de la excepción al bloque indicando el nombre de una política.
Mediante configuración se definen las políticas determinando que tratamientos se realizaran y en que orden.
Exception Handling Application Block encapsula los tratamientos en clases re-utilizables (handlers), incluye tres implementanciones: Registrar la excepción (logging), reemplazar la excepción y envolver la excepción.
Es posible extender el bloque creando nuevos handlers.
Exception Handling Application Block es parte de Enterprise Library 4.1.

Ejemplos de código

El código de manejo de excepciones que va en la clausula catch es estándar, se copia tal cual en cada método en que requiera capturar una excepción. Su única función es delegar el manejo de la excepción al bloque.
Para esto usa el método HandleException de la fachada estática ExceptionPolicy. Se pasan como parámetros la excepción y el nombre de la política que se usara para tratar la excepción. El método devuelve un valor boolean que indica si se debe relanzar la excepción original, en cuyo caso se ejecuta la instrucción throw.

try
{
    // Lógica de la aplicación
}
catch (Exception ex)
{
    bool rethrow = ExceptionPolicy.HandleException(ex, "Capa datos");
    if (rethrow)
        throw;
}

Es aconsejable definir los nombres de las políticas como constantes.

Configuración de las políticas

Las políticas se identifican por nombre y se definen en el archivo de configuración. Es recomendable utilizar la Herramienta de Configuración de Enterprise Library para realizar la configuración.

En la imagen se puede ver la política "Capa Datos" que define el tratamiento que recibiran las excepciones arrojadas por los métodos de la capa de datos de una aplicación ficticia.
Dentro de la política se definen tipos de excepción. Esto permite tratar de manera diferente cada tipo de excepción. El bloque determina el tipo de excepción de manera similar a como lo hace el runtime .NET con las clausulas catch. Intenta primero buscar el tipo exacto de la excepción, si no lo encuentra entonces busca la clase base mas cercana. Si no encuentra ninguna clase base, no se realiza ningun tratamiento y se relanza la excepción original.
Dentro de cada tipo de excepción se definen los handlers, que son las clases que implementan los tratamientos que se realizan a una excepción. Puede determinar el orden de ejecución de los handlers haciendo click con el botón derecho del mouse sobre un handler y aparecera el menú contextual en el cual hay opciones para moverlo hacia arriba o hacia abajo en la lista.

Como parte de Exception Handling Application Block se incluyen tres handlers que se describen a continuación:

Replace

Este handler reemplaza la excepción original por otra, posiblemente con la intención de evitar que se filtren datos privados. En el parámetro ExceptionMessage se debe indicar el mensaje de error de la nueva excepción. En el parámetro ReplaceExceptionType debe indicar el tipo de la nueva excepción. Esta debe tener un constructor que acepte un parámetro string para el mensaje de error. Se incluyen ademas los parámetros ExceptionMessageResourseName y ExceptionMessageResourseType que permiten localizar el mensaje de error.

Wrap

Este handler envuelve una excepción original con otra posiblemente mas significativa. En el parámetro ExceptionMessage se debe indicar el mensaje de error de la nueva excepción. En el parámetro WrapExceptionType debe indicar el tipo de la excepción que envolverá la excepción inicial, esta excepción debe tener un constructor que acepte dos parámetros, un string para el mensaje y un Exception para la excepción inicial. Se incluyen ademas los parámetros ExceptionMessageResourseName y ExceptionMessageResourseType que permiten localizar el mensaje de error.

Logging

Este handler permite registrar la excepción en el log, utiliza Logging Application Block por lo tanto debe configurar también este bloque. Los parámetros del handler Logging son:

  • EventID: El numero de identificación del evento que va a registrar, por defecto 1000.
  • FormatterType: La clase que transforma la excepción en al texto que se registra en el log.
  • LogCategory: El nombre de la categoría al que se asigna el evento.
  • Priority: Prioridad.
  • Severity. Severidad del evento, por defecto Error.
  • Title: El titulo del evento.
  • UseDefaultLogger: Determina que archivo utilizar.

Para una mejor compresión de estos parámetros lea Logging Application Block.

Para cada tipo de excepción dentro de una política se debe definir el parámetro PostHandlingAction. Este parámetro determina si se lanza una nueva excepción una vez que se ejecutaron todos los handlers y ademas que valor devuelve el método HandleException. Los valores posibles son:

  • ThrowNewException: Lanza la excepción que devuelve el ultimo handler y HandleException devuelve false. Se utiliza principalmente cuando envuelve o reemplaza la excepción original, en estos casos es necesario lanzar la nueva excepción y evitar que se relance la expcepcion original.
  • NotifyRethrow: HandleException devuelve true, lo que significa que se relanzara la misma excepción inicial. Se utiliza NotifyRethrow por ejemplo si quiere registrar una excepción en el log y luego relanzarla sin perder información de la stack trace original.
  • None: HandleException devuelve false, es decir no se relanzara la excepción. La excepción se considera controlada y el código continua ejecutándose normalmente. Se utiliza cuando quiere realizar un tratamiento y luego continuar la aplicación silenciosamente.
Mensaje para el usuario.

Cada vez que se invoca el método HandleException se crea un identificador GUID y es pasado a cada uno de los handlers que se ejecutan.
Cuando utiliza el handler Logging se incluye este identificador en el formato que se registra en el log.
Por otro lado, cuando reemplaza o envuelve una excepción debe definir el mensaje de texto para esta nueva excepción. Puede utilizar el marcador {handlingInstanceID} en el mensaje de texto para que sea reemplazado por el identificador.
Esto dos comportamientos permiten, por ejemplo, que si reemplaza una excepción con otra de mas alto nivel que incluye un mensaje para mostrar al usuario, puede incluir en el mensaje el código de identificación. El usuario podrá contactarse con soporte técnico e indicar este código. Con este código el equipo de diagnostico podrá buscar en el log y encontrar la excepción original.
En la aplicación Quickstart que se incluye como ejemplo el código fuente de un handler que muestra un mensaje de error al usuario que incluye el código de identificación, puede usarse este handler en la capa de presentación de su aplicación.

Recomendaciones para le manejo de excepciónes

En general se recomienda capturar una excepción usando la clausula catch solo cuando se va a hacer algo, sino es mejor dejar que la excepción se propague al llamador. De esta forma se evitan posibles perdidas de información de depuración que ocurren cuando se capturan y relanzan excepciones.
Generalmente las excepciones solo se capturan en los limites lógicos de una aplicación, por ejemplo: Capas, servicios, componentes o ensamblado.

Cuando ocurre un excepcione la aplicación debe reaccionar de manera adecuada para cumplir sus requerimientos de seguridad, diagnostico y usabilidad. Algunos ejemplos de reacciones posibles son:

  • Tratar de recuperarse de la excepción, por ejemplo reintentando la operación.
  • Ejecutar código de limpieza.
  • Mostrar un mensaje de error al usuario.
  • Registrar la excepción en el log para posterior diagnostico.
  • Notificar la excepción para que no pase inadvertida, por ejemplo vía email o un evento WMI.
  • Reemplazar la excepción por otra de mas alto nivel para evitar que se filtre información por razones de privacidad o seguridad.
  • Envolver la excepción inicial con una mas significativa para entregar una mejor información de contexto sin perder los detalles de la excepción original.

Los beneficios de usar Exception Handling Application Block son:

  • Encapsula la lógica para realizar las tareas mas comunes de manejo de excepciones en clases reutilizables (handlers). Esto permite evitar múltiples lineas de código repetitivo.
  • Hace posible definir políticas de manejo de excepciones que determinan paso a paso que tratamientos aplicar a una excepción, y luego aplicar estas políticas a un gran numero de métodos.
  • Las políticas de manejo de excepciones se definen en el archivo de configuración, esto permite modificar el manejo de excepciones sin recompilar la aplicación.
  • Disminuye la curva de aprendizaje, el programador no necesita interiorizarse de como manejar las excepciones, solo copia código estándar en la clausula catch indicando el nombre de una política. Posteriormente el arquitecto define que hace cada política en el archivo de configuración.
  • Uniforma el código de tratamiento de excepciones en toda una aplicación o incluso todo los proyectos de la compañía.
  • Es posible una integración con Policy Injection Application Block para separar completamente el código de manejo de excepciones de la lógica de la aplicación. En tal caso ni siquiera el código estándar de la clausula catch es necesario.
  • Si se incluyese en cada método el código de manejo de excepciones, a la hora de modificarlo habría que visitar cada archivo con la posibilidad de obviar algunos.
Excepciones internas de Exception Handling Application Block

Pueden ocurrir excepciones dentro del propio código de manejo de excepciones. Cuando la excepción se debe a un problema de configuración se lanza ConfigurationErrorsException, en otro caso se lanza ExceptionHandlingException. Estas excepciones son registradas en el registro de eventos de Windows si ha configuración de esta forma la instrumentación de Enterprise Library.

Funcionamiento interno

La aplicación llama al método HandleException en la clausula catch indicando el nombre de la política a usar. Este método crear un objeto ExceptionPolicyImpl que representa la política y lo configura según el archivo de configuración. El objeto ExceptionPolicyImpl contiene una colección de objetos ExceptionPolicyEntry que representa cada una de las excepciones definidas para esa política en la configuración. Cada objeto ExceptionPolicyEntry contiene una colección de IExceptionHandler que representan los handlers a aplicar a la excepción Esta colección esta ordenada indicando la secuencia en que se ejecutaran los handlers. Cada handler recibe como parámetro un objeto Exception y devuelve un objeto Exception, el primer handler recibe como parámetro la excepción orginal, el segundo handler recibe la excepcion que devuelve el primer handler. Por lo tanto el orden de los handlers es muy importante, no da lo mismo ubicar primero Logging y luego Wrap que hacerlo a la inversa.

Extender

Puede incorporar un nuevo handler creando una clase que implemente IExceptionHandler. Esta interfaz tiene un solo método HandleException que recibe como parámetros la excepción y un Guid que se genera aleatoriamente cada vez que se procesa una excepción. El método HandleException devuelve una Excepción que puede ser la misma que recibió u otra distinta por ejemplo si la reemplazo o la envolvió. Para poder configurar el handler usando le herramienta de configuración de Enterprise Library debe marcar la clase con el atributo ConfigurationElementType y pasar como parámetro el tipo CustomHandlerData. Esto le permitirá configurar su handler definiendo pares clave valor. Debe agregar un constructor que reciba como parámetro un objeto NameValueCollection.

[ConfigurationElementType(typeof(CustomHandlerData))]
public class MiPropioExceptionHandler : IExceptionHandler
{
  public MiPropioExceptionHandler(NameValueCollection ignore)
  {
  }

  public Exception HandleException(Exception exception, Guid handlingInstanceId)
  {
     // Perform processing here. The exception returned will be passed to the next
     // exception handler in the chain. 
  }
}

En la aplicación de ejemplo Quickstart hay un handler que muestra un mensaje de error al usuario usando un MessageBox.

Otra tipo de extensión es implementar un nuevo Formatter. La clase Formatter es la encargada de transformar a texto las excepciones antes de registrarlas en el log cuando se usa el handler Logging.

Vea tambien
Links