2010-11-06 74 views
28

¿Cuál es el enfoque sugerido o la mejor práctica para manejar excepciones en aplicaciones escalonadas?Manejo de excepciones en aplicaciones n-tier?

  • ¿Dónde se debe colocar try/catch blocks?
  • ¿Dónde debería implementar el registro?
  • ¿Hay algún patrón sugerido para administrar excepciones en aplicaciones de n niveles?

Considere un ejemplo simple. Suponga que tiene una interfaz de usuario, que llama a una capa de negocio, que llama a una capa de datos:

//UI 
protected void ButtonClick_GetObject(object sender, EventArgs e) 
{ 
    try { 
     MyObj obj = Business.GetObj(); 
    } 
    catch (Exception ex) { 
     Logger.Log(ex); //should the logging happen here, or at source? 
     MessageBox.Show("An error occurred"); 
    } 
} 

//Business 
public MyObj GetObj() 
{ 
    //is this try/catch block redundant? 
    try { 
     MyObj obj = DAL.GetObj(); 
    } 
    catch (Exception ex) { 
     throw new Exception("A DAL Exception occurred", ex); 
    } 
} 

//DAL 
public MyObj GetObj() 
{ 
    //Or is this try/catch block redundant? 
    try { 
     //connect to database, get object 
    } 
    catch (SqlException ex) { 
     throw new Exception("A SQLException occurred", ex); 
    } 
} 

¿Qué críticas le haría del manejo de excepciones arriba?

gracias

+0

no hay una respuesta aceptada? :/ – Vbp

Respuesta

3

La primera cosa para arreglar serían nunca lanzar un general Exception.

La segunda, a menos que haya realmente una buena razón para envolver una excepción, solo tiene throw; en lugar de throw new... en su cláusula catch.

La tercera (y esta no es una regla rápida & rápida), no detecta Excepciones generales en ningún punto debajo de la capa de UI. La capa UI debe detectar Excepciones generales para que el usuario final pueda mostrar un mensaje fácil de usar, no los detalles técnicos de lo que explotó. Si captas una excepción general más profunda en las capas, es posible que se trague involuntariamente y crea errores que son bastante difíciles de rastrear.

+0

Ok, gracias. ¿Qué pasa con el try/catch en la capa de negocios que efectivamente no hace nada? ¿Debería siempre envolver todas las llamadas a métodos que arrojen excepciones en try/catch independientemente, o debería solo usar try/catch en alguna capa superior? – flesh

17

Mi regla general es detectar excepciones en el nivel superior y registrarlas (o informar de alguna otra forma) allí, porque aquí es donde tiene la mayor información sobre el error, sobre todo, el seguimiento completo de la pila.

Puede haber algunas razones para la captura de excepciones en otros niveles, sin embargo:

  1. La excepción es en realidad maneja. Eg. una conexión falló, pero el nivel la vuelve a intentar.
  2. La excepción se vuelve a lanzar con más información (que no está ya disponible al mirar la pila). P.ej. el DAL podría informar a qué DB estaba intentando conectarse, lo que SqlException no le diría.
  3. La excepción se traduce en una excepción más general que es parte de la interfaz para ese nivel y puede (o no) manejarse más arriba. P.ej. el DAL puede detectar errores de conexión y arrojar un DatabaseUnavailableException. El BL puede ignorar esto para las operaciones que no son críticas o puede dejar que se propaguen para las que sí lo son. Si el BL atrapó SqlException, estaría expuesto a los detalles de implementación del DAL. En cambio, la posibilidad de lanzar DatabaseUnavailableException es parte de la interfaz del DAL.

Registrar el mismo error en varios niveles generalmente no es útil, pero puedo pensar en una excepción: cuando un nivel inferior no sabe si un problema es crítico o no, puede registrarlo como advertencia. Si un nivel superior decide que es crítico, puede registrar el mismo problema que un error.

+2

Gracias, esa es una respuesta realmente útil. – flesh

2

El manejo de excepciones es difícil en cualquier aplicación. Debe pensar en todas y cada una de las excepciones y entrar rápidamente en un patrón que funcione para usted. Trato de grupo de excepciones en una de las siguientes categorías ...

  • excepciones que sólo debería ocurrir si su código es incorrecto (o usted no entiende, o que no tienen control sobre ellos):

Ejemplo: Tal vez su framework de persistencia se requiere para atrapar excepciones de SQL que podrían derivarse de SQL mal formado, sin embargo, usted está ejecutando una consulta duro codificado.

Manejo: En mi experiencia la mayoría de las excepciones caen en esta categoría. Por lo menos, inicie sesión. Mejor aún, envíelos a un servicio de manejo de excepciones que los registra. Luego, en el futuro, si decide que desea registrarlos de manera diferente o hacer algo diferente con ellos, puede cambiarlo en un solo lugar. Tal vez también quiera enviar una bandera hasta la capa de la interfaz de usuario diciendo que se produjo algún tipo de error y que deberían volver a intentar su operación. Tal vez envíes un administrador por correo.

También deberá devolver algo a las capas superiores para que la vida en el servidor continúe. Tal vez este es un valor predeterminado, o tal vez esto es nulo. Tal vez tienes alguna manera de cancelar toda la operación.

Otra opción es dar al servicio de manejo de excepciones dos métodos de manejo. Un método handleUnexpectedException() notificaría al usuario pero no volvería a lanzar una excepción, y podría devolver un valor predeterminado si tiene la capacidad de desenrollar la pila usted mismo o continuar de alguna manera. Un método handleFatalException() notificaría al usuario y volvería a generar algún tipo de excepción para que pueda dejar que la excepción arroje la pila por usted.

  • excepciones que realmente son causadas por el usuario:

Ejemplo: El usuario está intentando actualizar un widget foobar y darle un nuevo nombre, pero un widget foobar ya existe con el nombre que quieren.

Manejo: En este caso debe devolver la excepción al usuario. En este caso, puede continuar lanzando la excepción (o mejor, ni siquiera atraparla) hasta que llegue a la capa UI y luego la capa UI debería saber cómo manejar la excepción. Asegúrese de documentar estas excepciones para que usted (o quien esté escribiendo su UI) sepa que existen y sabe esperarlas.

  • excepciones de que en realidad se puede manejar:

Ejemplo: realiza una llamada de servicio remoto y los tiempos de servicio remoto fuera, pero que saben que tienen un historial de hacerlo y usted debe solo rehace la llamada.

Manejo: A veces estas excepciones comienzan en la primera categoría. Después de que su aplicación haya estado en libertad por un tiempo, se da cuenta de que realmente tiene una buena forma de manejarla.A veces, como con excepciones de bloqueo optimista o excepciones interrumpidas, capturar la excepción y hacer algo con ella es solo una parte del negocio. En estos casos, maneje la excepción. Si su lenguaje (estoy pensando en Java) distingue entre las excepciones marcadas y las no marcadas, recomendaría que estas siempre sean excepciones marcadas.

Para abordar su pregunta anterior, delegaría la excepción inicial a un servicio que notificaría al usuario y dependiendo de qué tipo de objeto es MyObj (por ejemplo, configuraciones) podría dejar que esto sea una excepción no fatal y devolver un valor predeterminado valor o si no puedo hacer eso (por ejemplo, cuenta de usuario) entonces podría dejar que esto sea una excepción fatal, así que no tengo que preocuparme por ello.

11

Estas son algunas reglas que siga relacionados con el manejo de excepciones:

  • excepciones sólo deben quedar atrapados en las capas que pueden implementar la lógica para el manejo de ellos. La mayoría de los casos esto sucede en las capas superiores. Como regla general, antes de implementar una captura dentro de una capa, pregúntese si la lógica de cualquiera de las capas superiores depende de algún modo de la existencia de la excepción. Por ejemplo, en la capa empresarial, es posible que tenga la siguiente regla: cargar datos de un servicio externo si el servicio está disponible, si no, cargarlo desde la memoria caché local. Si al llamar al servicio arroja una expiación, puede capturarlo e ingresarlo en el nivel BL y luego devolver los datos desde la caché. La capa superior de UI no tiene que realizar ninguna acción en este caso. Sin embargo, si tanto el servicio como la llamada de caché fallan, la excepción debería ir al nivel de UI para que se pueda mostrar un mensaje de error al usuario.
  • todas las excepciones dentro de una aplicación deben capturarse y si no hay una lógica especial para manejarlas, se deben registrar al menos. Por supuesto, esto no significa que deba envolver el código de todos los métodos en los bloques try/catch. En su lugar, cualquier tipo de aplicación tiene un controlador global para excepciones no detectadas. Por ejemplo, en aplicaciones de Windows, se debe implementar el evento Application.ThreadException. En las aplicaciones ASP .Net, debe implementarse el controlador de eventos Application_Error de global.asax. Estos lugares son los lugares más altos donde puede detectar excepciones en su código. En muchas aplicaciones, este será el lugar donde captará la mayoría de las excepciones y, además del registro, aquí también probablemente implementará una ventana de mensaje de error genérico y amigable que se presentará al usuario.
  • puede implementar la funcionalidad try/finally donde lo necesite sin implementar un bloque catch. Un bloque catch no debe implementarse si no necesita implementar ninguna lógica de manejo de excepciones. Por ejemplo:
SqlConnection conn = null; 
try 
{ 
    conn = new SqlConnection(connString); 
    ... 
} 
// do not implement any catch in here. db exceptions logic should be implemented at upper levels 
finally 
{ 
    // finalization code that should always be executed. 
    if(conn != null) conn.Dispose(); 
} 
  • si necesita volver a lanzar una excepción de un bloque catch hacer que simplemente usando throw;. Esto asegurará que se preserve la traza de la pila. El uso de throw ex; restablecerá el seguimiento de la pila.
  • si necesita volver a generar una excepción con otro tipo que tenga más sentido para los niveles superiores, incluya la excepción original en la propiedad InnerException de la excepción recién creada.
  • si necesita lanzar excepciones desde su código use tipos de excepción significativos.
0

estoy con clase de excepción sepearate para cada capa (DALException, BLException, ...) para iniciar la sesión (por ejemplo: en el archivo) las excepciones a los límites de la capa (esto es para el administrador), porque el usuario sólo debe ver mensaje de error claro y comprensible. esas excepciones deberían tratarse en DAlBase que heredaba todos los niveles de la capa de acceso a los datos y así sucesivamente en todas las capas. con eso podemos centralizar el manejo de excepciones en pocas clases y el desarrollador solo lanzará layerexception (e.g: DALException) ver más información MultiTier Exception Handling.