Pregunta

Siempre sentí que esperar que se lanzaran excepciones regularmente y usarlas como lógica de flujo era algo malo. Las excepciones se sienten como deberían ser, bueno, la " excepción " ;. Si está esperando y planeando una excepción, eso parece indicar que su código debe ser refactorizado, al menos en .NET ...
Sin embargo. Un escenario reciente me dio pausa. Publiqué esto en msdn hace un tiempo, pero me gustaría generar más discusión al respecto y ¡este es el lugar perfecto!

Por lo tanto, supongamos que tiene una tabla de base de datos que tiene una clave externa para otras tablas (en el caso en que originalmente se inició el debate, había 4 claves externas que lo señalaban). Desea permitir que el usuario elimine, pero solo si NO hay referencias de clave externa; no quieres eliminar en cascada.
Normalmente solo hago una comprobación para ver si hay referencias, y si las hay, informo al usuario en lugar de hacer la eliminación. Es muy fácil y relajante escribir que en LINQ, ya que las tablas relacionadas son miembros del objeto, por lo que Section.Projects and Section.Categories y etcétera es agradable escribir con intellisense y todo ...
Pero el hecho es que LINQ tiene que golpear potencialmente las 4 tablas para ver si hay alguna fila de resultados que apunte a ese registro, y golpear la base de datos es siempre una operación relativamente costosa.
El liderazgo en este proyecto me pidió que lo cambiara para simplemente capturar una SqlException con un código de 547 (restricción de clave externa) y tratarlo de esa manera.

Yo estaba ...
resistente .

Pero en este caso, probablemente sea mucho más eficiente tragar la sobrecarga relacionada con la excepción que tragar los 4 hits de la mesa ... Especialmente porque tenemos que hacer la verificación en todos los casos, pero nos ahorramos la excepción en el caso cuando no hay niños ...
Además, la base de datos realmente debería ser la responsable del manejo de la integridad referencial, ese es su trabajo y lo hace bien ...
Así que ganaron y yo lo cambié.

Sin embargo, en cierto nivel todavía me parece mal .

¿Qué piensan ustedes acerca de esperar y manejar intencionalmente las excepciones? ¿Está bien cuando parece que será más eficiente que verificar de antemano? ¿Es más confuso para el próximo desarrollador que mira tu código, o menos confuso? ¿Es más seguro, ya que la base de datos podría conocer las nuevas restricciones de clave externa que el desarrollador podría considerar no agregar para verificar? ¿O es una cuestión de perspectiva sobre qué cree exactamente que es la mejor práctica?

¿Fue útil?

Solución

Wow,

Primero que nada, ¿puedes resumir la pregunta un poco, si bien fue bueno leer una pregunta bien pensada y explicada, eso fue mucho para resumir?

La respuesta corta es " sí " ;, pero puede depender.

  • Tenemos algunas aplicaciones en las que tenemos mucha lógica empresarial vinculada a las consultas SQL (¡no a mi Gov de diseño!). Si esta es la forma en que está estructurado, la administración puede ser difícil de convencer de lo contrario, ya que " ya funciona " ;.
  • En esta situación, ¿realmente hace un gran problema? Ya que todavía es un viaje a través del cable y la espalda. ¿Hace mucho el servidor antes de darse cuenta de que no puede continuar (es decir, si hay una secuencia de transacciones que tienen lugar en su acción, se cae a la mitad, perdiendo tiempo?).
  • ¿Tiene sentido hacer el control en la interfaz de usuario primero? ¿Ayuda con tu aplicación? Si proporciona una experiencia de usuario más agradable? (Es decir, he visto casos en los que se paso a través de varios pasos en un asistente, se inicia, luego se cae, cuando tenía toda la información que necesitaba para caer después del paso 1).
  • ¿La concurrencia es un problema? ¿Es posible que el registro se pueda eliminar o editar o lo que sea antes de que se realice su confirmación (como en el clásico File.Exists boo-boo)?

En mi opinión:

Haría ambos . Si puedo fallar rápido y proporcionar una mejor experiencia de usuario, genial. Todas las excepciones esperadas de SQL (o cualquier otra) deben ser capturadas y realimentadas adecuadamente de todos modos.

Sé que existe un consenso en el sentido de que no se deben usar excepciones que no sean circunstancias excepcionales , pero recuerde que aquí estamos cruzando los límites de las aplicaciones, no esperamos nada . Como dije, esto es como el File.Exists , no tiene sentido, se puede eliminar antes de acceder a él de todos modos.

Otros consejos

Su ventaja es absolutamente correcta. Las excepciones no son solo por una vez en situaciones de luna azul, sino específicamente para informar resultados que no sean los esperados.

En este caso, la verificación de la clave foránea seguirá teniendo lugar, y las excepciones son el mecanismo mediante el cual se le puede notificar.

Lo que NO debe hacer es capturar y suprimir las excepciones con una declaración general. Hacer el manejo de excepciones de grano fino es específicamente por qué se diseñaron las excepciones en primer lugar.

Creo que tiene razón, las excepciones solo deben usarse para manejar resultados inesperados , aquí está utilizando una excepción para tratar un posible resultado esperado, debe tratar este caso de manera explícita pero aún así detectar la excepción para mostrar un posible error.

A menos que esta sea la forma en que se maneja este caso en todo el código, me pondría del lado tuyo. El problema de rendimiento solo debe ser mencionado si es realmente un problema, es decir, dependerá del tamaño de esas tablas y del número de veces que se utiliza esta función.

Buena pregunta. Sin embargo, me parece que las respuestas son aterradoras

Una excepción es un tipo de GOTO.
No me gusta usar excepciones de esta manera porque eso lleva al código de espagueti.
Simple como eso.

No hay nada de malo en lo que estás haciendo. Las excepciones no son necesariamente "excepcionales". Existen para permitir que los objetos que llaman tengan un manejo de errores muy preciso según sus necesidades.

La captura de la SqlException específica es lo correcto. Este es el mecanismo por el cual SQL Server comunica la condición de clave externa. Incluso si podría favorecer un uso diferente del mecanismo de excepción, así es como lo hace SQL Server.

Además, durante la verificación de las cuatro tablas, algún otro usuario puede agregar un registro relacionado antes de que se complete la verificación pero después de leer esa tabla.

Recomiendo llamar a un procedimiento almacenado que verifique si hay dependencias, y luego eliminar si no hay ninguna. De esa manera, se realiza la verificación de integridad.

Por supuesto, es probable que desee un procedimiento almacenado diferente para las eliminaciones singleton frente a la eliminación por lotes ... La eliminación por lotes podría buscar filas secundarias y devolver un conjunto de registros que no eran elegibles para una eliminación masiva (había filas hijo).

No me gusta ver excepciones en todas partes en un programa, pero en este caso usaría el mecanismo de excepción.

Incluso si realiza la prueba en LINQ, tendrá que detectar la excepción en caso de que alguien inserte un registro secundario mientras prueba la integridad con LINQ. Ya que tiene que verificar la excepción de todos modos, ¿por qué duplicar el código?

Otro punto es que este tipo de " rango corto " La excepción no causa problemas de mantenimiento, ni hace que su programa sea más difícil de leer. Tendrá el intento, la llamada a SQL para eliminar y el retén, todo eso dentro de 10 líneas de código, juntos. La intención es obvia.

No es como lanzar una excepción que se debe capturar. 5 El procedimiento se encuentra en la parte superior de la pila, con los problemas de asegurarse de que todos los objetos intermedios se hayan atendido (liberado) correctamente.

Las excepciones no siempre son una respuesta mágica, pero no me sentiría mal si las utilizo en esta situación.

Mis dos centavos,

Yves

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top