En serio, ¿cómo puede manejar todas esas excepciones sin volverse loco? ¿He leído demasiados artículos sobre el manejo de excepciones o qué? I intenté refactorizar esto un par de veces y cada vez parece que termino con algo aún peor. Tal vez debería admitir que las excepciones suceden y simplemente disfrutar de la codificación de la ruta feliz feliz? ;) Entonces, ¿qué hay de malo con este código (además del hecho de que era lo suficientemente flojo como para lanzar Exception
en lugar de algo más específico)? Y, por supuesto, no seas fácil conmigo.Cuando tantas cosas pueden salir mal, todo lo que hace es intentar, intentar, probar
public void Export(Database dstDb)
{
try
{
using (DbConnection connection = dstDb.CreateConnection())
{
connection.Open();
DbTransaction transaction = connection.BeginTransaction();
try
{
// Export all data here (insert into dstDb)
transaction.Commit();
}
catch (SqlException sqlex)
{
ExceptionHelper.LogException(sqlex);
try
{
transaction.Rollback();
}
catch (Exception rollbackEx)
{
logger.Error("An exception of type " + rollbackEx.GetType() +
" was encountered while attempting to roll back the transaction.");
}
throw new Exception("Error exporting message " + Type + " #" + Id + ": [" + sqlex.GetType() + "] " + sqlex.Message, sqlex);
}
catch (Exception ex)
{
try
{
transaction.Rollback();
}
catch (Exception rollbackEx)
{
logger.Error("An exception of type " + rollbackEx.GetType() +
" was encountered while attempting to roll back the transaction.");
}
throw new Exception("Error exporting message " + Type + " #" + Id + ": [" + ex.GetType() + "] " + ex.Message, ex);
}
}
try
{
Status = MessageStatus.FINISHED;
srcDb.UpdateDataSet(drHeader.Table.DataSet, HEADERS,
CreateHeaderInsertCommand(), CreateHeaderUpdateCommand(), null);
}
catch (Exception statusEx)
{
logger.ErrorException("Failed to change message status to FINISHED: " +
Type + " #" + Id + ": " + statusEx.Message, statusEx);
}
}
catch (Exception importEx)
{
try
{
Status = MessageStatus.ERROR;
srcDb.UpdateDataSet(drHeader.Table.DataSet, HEADERS,
CreateHeaderInsertCommand(), CreateHeaderUpdateCommand(), null);
}
catch (Exception statusEx)
{
logger.ErrorException("Failed to change message status to ERROR: " +
Type + " #" + Id + ": " + statusEx.Message, statusEx);
}
AddErrorDescription(importEx.Message);
throw new Exception("Couldn't export message " + Type + " #" + Id + ", exception: " + importEx.Message, importEx);
}
}
Btw. Tantas veces intenté realmente ser lo más específico posible al formular preguntas; el resultado fue que no hubo visitas, ni respuestas, ni idea de cómo resolver el problema. Esta vez pensé en todas las ocasiones en las de otra persona pregunta me llamó la atención, supongo que esto era lo correcto a hacer :)
Actualización:
He intentado poner algunos de los consejos en práctica y esto es lo que se me ocurrió hasta ahora. Decidí modificar ligeramente el comportamiento: cuando no es posible establecer el estado del mensaje en FINISHED después de la exportación exitosa, lo considero como un trabajo que no se ha realizado del todo y restituyo y lanzo una excepción. Si todavía les queda algo de paciencia, háganme saber si es mejor. O arroja algunas críticas más a mí. Por cierto. Gracias por todas las respuestas, analicé cada una de ellas.
Lanzar una instancia de System.Exception
no me pareció correcto, así que me deshice de eso, como se sugirió, y en su lugar decidí introducir una excepción personalizada. Esto, por cierto, tampoco parece correcto, ¿exagerado? Esto parece estar bien con los métodos públicos, pero un poco sobre-diseñado para un miembro privado, y aún así quiero saber que hubo un problema con el cambio de estado del mensaje en lugar de un problema con la conexión a la base de datos o algo así.
puedo ver dos formas de extraer los métodos aquí, pero todos ellos parecen mezclar responsabilidades que jgauffin mencionan en his comment: gestión de conexión de base de operaciones, manejo de bases de datos, lógica de negocio (datos de exportación). Diga el método ChangeStatus
- es un cierto nivel de abstracción - cambia el estado de un mensaje y no le interesa cómo sucede esto, cómo persiste el mensaje, etc. Tal vez debería usar un patrón de Data Mapper para separar más responsabilidades , pero en este escenario aún bastante simple pensé que me saldría con Active Record. ¿Tal vez todo el diseño es tan enrevesado en este momento, que no veo dónde hacer los cortes?
public void Export(Database dstDb)
{
try
{
using (DbConnection connection = dstDb.CreateConnection())
{
connection.Open();
using (DbTransaction transaction = connection.BeginTransaction())
{
// Export all data here (insert into dstDb)
ChangeStatus(MessageStatus.FINISHED);
transaction.Commit();
}
}
}
catch (Exception exportEx)
{
try
{
ChangeStatus(MessageStatus.ERROR);
AddErrorDescription(exportEx.Message);
}
catch (Exception statusEx)
{
throw new MessageException("Couldn't export message and set its status to ERROR: " +
exportExt.Message + "; " + statusEx.Message, Type, Id, statusEx);
}
throw new MessageException("Couldn't export message, exception: " + exportEx.Message, Type, Id, exportEx);
}
}
private void ChangeStatus(MessageStatus status)
{
try
{
Status = status;
srcDb.UpdateDataSet(drHeader.Table.DataSet, HEADERS,
CreateHeaderInsertCommand(), CreateHeaderUpdateCommand(), null);
}
catch (Exception statusEx)
{
throw new MessageException("Failed to change message status to " + status + ":" + statusEx.Message, statusEx);
}
}
Es el precio que paga por crear una aplicación robusta. – ChrisF
De acuerdo con ChrisF. Mi único comentario es que deberías lanzar una subclase de 'Excepción', que ya sabes. Pero tiene dos beneficios, obtienes una señal más clara cuando la atrapas más tarde y puedes mover la construcción de cuerdas a un lugar en lugar de repetirla muchas veces. – unholysampler
¿No sería bueno si el compilador le dijera lo que necesita para capturar – CurtainDog