2009-08-29 24 views
8

Tengo este tipo de formatoError al manejar ¿Debo arrojar una excepción? ¿O manejar en la fuente?

asp.net MVC View -> Service Layer -> Repository.

Por lo tanto, la vista llama a la capa de servicio que tiene lógica comercial/de validación en ella, que a su vez llama al Repositorio.

Ahora mi método de capa de servicio generalmente tiene un tipo de retorno de bool para que pueda devolver verdadero si la consulta de la base de datos ha sido satisfactoria. O si falló Luego se muestra un mensaje genérico para el usuario.

Por supuesto, registraré el error con elmah. Sin embargo, no estoy seguro de cómo debería llegar a este punto.

Me gusta en este momento mi Repositorio tiene tipos de devolución anulados para actualizar, crear, eliminar.

Entonces, si una actualización falla, ¿debo probar/atrapar en mi repositorio que arroja el error, entonces mi capa de servicio lo detecta y elmah señaliza y devuelve falso?

O debería tener estos métodos de repositorio devuelvan un "bool", intente/capture el error en el repositorio y luego devuelva "verdadero" o "falso" a la capa de servicio que a su vez devuelve "verdadero" o "falso" a la vista?

El manejo de excepciones todavía me confunde cómo manejar los errores y cuándo tirar, y cuándo detectar el error.

Respuesta

15

La regla de oro que siempre uso es:

  • A bajos niveles, lanza cuando una operación no se puede completar debido a circunstancias excepcionales.
  • En capas intermedias, capture varios tipos de excepciones y vuelva a envolver en un solo tipo de excepción.
  • Manejar excepciones en el último momento responsable.
  • DOCUMENTO!

He aquí un ejemplo en pseudocódigo para un multi-capa de aplicación ASP.NET MVC (IU, controlador, lógica, Seguridad, Repositorio):

  1. El usuario hace clic botón de envío.
  2. La acción del controlador se ejecuta y llama a la capa Logic (business).
  3. método lógica pone en seguridad con las credenciales de usuario actual
    • usuario no es válido
      • capa de Seguridad lanza SecurityException
      • capturas capa de lógica, se envuelve en LogicException con un mensaje de error más genérico
      • Controlador captura LogicException, redirige a la página de Error.
    • usuario es válida y Seguridad vuelve
  4. capa lógica pone en el repositorio para completar la acción
    • Repositorio falla
      • Repositorio lanza RepositoryException
      • capa de lógica de las capturas, los abrigos en LogicException con un mensaje de error más genérico
      • El controlador detecta LogicException, lo redirecciona a la página de Error.
    • Repositorio tiene éxito
  5. capa lógica vuelve
  6. Controller redirige a la vista del éxito.

Observe que la capa Lógica solo arroja un único tipo de excepción: LogicException. Cualquier excepción de nivel inferior que se dispara se captura, envuelta en una nueva instancia de LogicException, que se lanza. Esto nos da muchas ventajas.

Primero, se puede acceder al seguimiento de la pila. En segundo lugar, las personas que llaman solo tienen que tratar con un solo tipo de excepción en lugar de múltiples excepciones. En tercer lugar, los mensajes de excepción técnica se pueden masajear para mostrar a los usuarios sin perder los mensajes de excepción originales. Por último, solo el código responsable de manejar la entrada del usuario puede conocer realmente cuál fue la intención del usuario y determinar qué respuesta apropiada es cuando falla una operación. El Repositorio no sabe si la IU debe mostrar la página de error o solicitar al usuario que intente nuevamente con diferentes valores. El controlador lo sabe.


Por cierto, nada dice que no puede hacer esto:

try 
{ 
    var result = DoSomethingOhMyWhatIsTheReturnType(); 
} 
catch(LogicException e) 
{ 
    if(e.InnerException is SqlException) 
    { 
    // handle sql exceptions 
    }else if(e.InnerException is InvalidCastException) 
    { 
    // handle cast exceptions 
    } 
    // blah blah blah 
} 
+1

Esta técnica presenta los siguientes problemas: 1) Cada vez que el ensamblaje de nivel inferior introduce más comprobaciones o más excepciones, todas las capas superiores deberían cambiar para ajustar la excepción correctamente; de ​​lo contrario, el envoltorio se convierte en un comodín glorificado y ganó no tiene mucho sentido 2) El uso de diferentes tipos de excepciones para diferentes errores aumenta la fidelidad del manejo de errores y si solo tenemos una excepción, donde sea que la maneje, tendrá que escribir su lógica de envoltura (si LogicException se debe a algo que manejarlo, de lo contrario, déjalo ir). Podría haber otros también ... –

+0

1) Baw. Tendría que atrapar estas nuevas excepciones de todos modos, entonces, ¿por qué llorar cuando está en un nivel inferior en lugar de en la interfaz de usuario? 2) Si haces muchas cosas, posiblemente puedas atrapar decenas de tipos de excepciones en el nivel de UI. Esto es increíblemente molesto y ensucia tu código con lógica en la interfaz de usuario que realmente no debería estar allí. Una vez más, está REDACTANDO la excepción original, por lo que aún puede CAPTAR un solo tipo y examinar InnerException para todas sus necesidades de fidelidad sin muchas capturas. 3) Podría haber otros rebuts, pero soy demasiado flojo para enumerarlos. – Will

+0

@Charles Prakash Dasari: Tener tipos de excepción distinguidos por tipo de falla no es realmente útil. Lo que es mucho más útil es que se distingan por estado del sistema. Por ejemplo, si se supone que una rutina debe actualizar un registro en la base de datos y no funciona, podría ser útil para un usuario saber si sucedió desde un tiempo de espera de bloqueo o un restablecimiento de conexión TCP, pero desde el punto de vista del programa, lo que es mucho más importante es saber si ... – supercat

0

En primer lugar, no hay una sola manera y ciertamente no es perfecta, así que no lo pienses demasiado.

En general, desea utilizar excepciones para casos excepcionales (las excepciones incurren en una sobrecarga de rendimiento, por lo que usarlas en exceso, especialmente en situaciones de "bucle" puede tener un impacto de perf). Entonces, digamos que el repositorio no se puede conectar al servidor de la base de datos por alguna razón. Entonces usarías una excepción. Pero si el repositorio ejecuta una búsqueda de algún objeto por id y el objeto no se encuentra, entonces querrá devolver nulo en lugar de lanzar una excepción diciendo que ese objeto con ID x no existe.

Lo mismo para la lógica de validación. Como está validando, se supone que a veces la entrada no se validará, por lo que en ese caso sería bueno devolver el mensaje falso del servicio de validación (o tal vez un tipo más complejo que incluya información adicional sobre por qué no validar). Pero si la lógica de validación incluye verificar si se toma un nombre de usuario o no y no puede hacer esto por algún motivo, entonces lanzaría una excepción.

Así dicen si falla la actualización debería tener un try/catch en mi repositorio que tiros el error, entonces mi capa de servicio coge y hace ELMAH señalización y devuelve falso?

¿Por qué fallaría la actualización? ¿Hay alguna razón perfecta para que esto ocurra que sea parte del proceso normal? Entonces no arroje una excepción si ocurre por una razón extraña (digamos que algo quitó el registro que se actualiza antes de que se actualizase), entonces una excepción se vuelve lógica. Realmente no hay forma de recuperarse de esta situación.

+0

"busca algún objeto por id y el objeto no se encuentra, entonces querrías devolver nulo" ... y luego esperar que la persona que llama verifique nulo? Si espera que la ID esté allí (no ingresada por el usuario), generalmente arroje. Si no está seguro, considere el patrón Try-Parse. Consulte http://blogs.msdn.com/kcwalina/archive/2005/03/16/396787.aspx – TrueWill

+0

Depende de quien llama para comprender los resultados esperados de llamar a la función. Si la persona que llama ignora el hecho de que la firma/documentación indica que la función puede devolver nulos, entonces la persona que llama tiene la culpa, no la función. La devolución de nulos es perfectamente válida en este caso IMOH. – BlackMael

+0

Por supuesto, también podría argumentar que la persona que llama no puede esperar excepciones, y no se molesta en ajustar la llamada con Try Catch. De cualquier forma, no puedes obligar a la persona que llama a hacer nada en absoluto. Por lo tanto, es un punto discutible utilizar las acciones de la persona que llama como un factor decisivo para devolver nulos o lanzar excepciones. – BlackMael

1

Me gusta pensar en el manejo de excepciones de esta manera: Usted define la firma de su método, en cuanto a lo que está esperando hacer. Ahora bien, si no puede hacer eso, entonces debe lanzar una excepción.Por lo tanto, si espera que algo falle según los datos de entrada que tiene (ignorando el estado ambiente), la firma de su método debe indicar si una operación ha tenido éxito o ha fallado. Pero si su método no espera fallar en función de la entrada que tiene (nuevamente, ignorando todo el otro estado ambiental), entonces una excepción estará en orden cuando el método falle.

Considere estas dos API:

int int.Parse(string integerValue); // In this case, the method will return int 
            // or it will die! That means your data must be 
            // valid for this method to function. 

bool int.TryParse(string integerValue, out number); // In this case, we expect the data 
                // we passed in might not be fully 
                // valid, hence a boolean. 
2

Mientras devolver un error (o éxito) código es a menudo la mejor manera, las excepciones tienen una gran ventaja sobre los códigos que regresan o silenciosamente suprimiendo errores: ¡al menos no puedes ignorarlos!

No abuse de las excepciones para un control de flujo simple: eso sería lo más tonto.

Pero si una función suya realmente se encuentra con un problema "excepcional", entonces definitivamente arroje una excepción. La persona que llama debe manejarlo de forma explícita y así saber qué está pasando, o lo bombardeará.

Devolver un código de error es peligroso ya que la persona que llama simplemente no se molesta en inspeccionar el código y posiblemente continúe, incluso si en la lógica de su aplicación, realmente hay algo mal y necesita ser tratado.

Entonces, no abuse de las excepciones, pero si ocurre una excepción real que requiera que la persona que llama haga algo al respecto, definitivamente recomendaría usar ese mecanismo para señalar condiciones excepcionales.

En cuanto a la gestión de excepciones: maneje aquellas que realmente puede manejar. P.ej. si intenta guardar un archivo y obtener una excepción de seguridad, muestre al usuario un cuadro de diálogo que le pida otra ubicación para guardar (ya que podría no tener permisos para guardar en donde quisiera).

Sin embargo, las excepciones realmente no se puede hacer frente a (? ¿Qué es lo que quiere hacer sobre una "excepción OutOfMemory", en realidad) se debe dejar sin tocar - tal vez una persona que llama más arriba en la pila puede manejar esas llamadas - o no. Marc

+0

Un enfoque que utilicé en sistemas que no tenían excepciones era combinar códigos de error de bloqueo con el requisito de que el código de la aplicación reconociera/reiniciara explícitamente un error antes de la siguiente operación de E/S o la próxima operación de E/S provocaría un fatal error (en un caso, tengo el error no fatal de capturar la pila y los valores de los parámetros, que luego pueden examinarse si se intenta otra operación de E/S antes de que se confirme/restablezca el error). Tal enfoque no encaja bien con los paradigmas "modernos", pero funciona bien si las excepciones no están disponibles. – supercat

Cuestiones relacionadas