Имеет вложенная Попытка/Выгода сигнал, что Вы не кодируете чисто? Интересно, потому что в моей выгоде я называю другой метод и если это перестало работать, я получаю другую ошибку периода выполнения, таким образом, я испытываю желание перенести те вызовы в выгоду с другой попыткой/выгодой снова. Интересно, нормально ли это, чтобы сделать это?
например.
catch (Exception ex)
{
transaction.VoidOrder(transactionID);
LogError(ex.ToString());
Response.Redirect("Checkout", false);
}
так VoidOrder
или даже LogError
методы могли разбомбить. Прямо сейчас, когда я звоню VoidOrder
, Я получаю пустой указатель касательно на transactionID
потому что это называет метод BL и в том, что метод BL, который я повторно бросаю так, я могу поймать его в этом более высоком уровне в коде выше. Но если я бросаю снова в выгоде затем, я должен поймать это также.
Вложенные блоки try / catch
неизбежны в приложениях верхнего уровня, которым необходимо вести журнал, отправлять сообщения или реагировать на исключения в других нетривиальные способы.
Есть способы сократить количество вложенных блоков (например, объединить обработку ошибок с помощью обработчика ASP.NET HttpApplication.Error
(также известного как Application_Error
)), но вы должны перехватить любые исключения, создаваемые вашим кодом обработки, и реализовать план резервного копирования на случай, если все остальное не удастся.
Еще одним решением проблемы вложенности может быть инкапсуляция логики try/catch для внутренних функций (LogError и т.д.) в самих функциях, вместо того чтобы полагаться на то, что вызывающая сторона их перехватит. Для LogError это имеет смысл, потому что вы, вероятно, захотите обрабатывать сломанный регистратор ошибок одинаково, независимо от того, кто пытается зарегистрировать ошибку.
Возможно, это вопрос, может ли кто-нибудь понять, почему вы используете вложенные try'ы/catches. Иногда их использование становится неизбежным, но я говорю, что обычно это некрасиво.
Вы должны подумать о том, что разделение задач - это обязательное условие.
Метод getControl не должен вставлять в него скрипт, равно как и метод save не должен делать больше, чем сохранять.
Изучите, что именно должны делать эти методы, и вы получите ответ. Обнуление порядка в случае ошибки выглядит не так уж неудобно. Звучит разумно.
Думали ли вы об использовании объекта TransactionScope вместо попытки отката транзакции вручную? Я знаю, что иногда это невозможно, но в большинстве случаев TransactionScope помогал мне. Таким образом, мне не приходилось управлять элементами отката вручную - откаты вручную в любом случае могут быть проблемой, потому что данные находятся в «нестабильном» состоянии, что может испортить всю вашу логику.
Вот как мы подходим к проблеме:
Все вызовы с уровня UI/codebehind на другие уровни используют try-catch, где мы всегда ловим пользовательское исключение. Все действия, выполняемые нижележащими уровнями, имеют свои try-catch, которые регистрируют, оборачивают и бросают пользовательское исключение. Пользовательский интерфейс может полагаться на это и искать обработанные исключения с дружественными сообщениями об ошибках.
Codebehind:
protected void btnSubmit_Click(object sender, EventArgs e)
{
//do something when a button is clicked...
try
{
MyBL.TakeAction()
}
catch(MyApplicationCustomException ex)
{
//display something to the user, etc.
ltlErrorPane.Text = ex.Message;
//or redirect if desired
if(ex.ErrorType == MyCustomErrorsType.Transactional)
{
Response.Redirect("~/Errors/Transaction.aspx");
}
}
}
BL:
На бизнес-уровне все операции, которые могут завершиться неудачей, используют try-catch, который регистрирует и оборачивает проблему, прежде чем бросить ее в пользовательский интерфейс.
public class MyBL
{
public static void TakeAction()
{
try
{
//do something
}
catch(SpecificDotNetException ex)
{
//log, wrap and throw
MyExceptionManagement.LogException(ex)
throw new MyApplicationCustomException(ex, "Some friendly error message", MyCustomErrorsType.Transactional);
}
finally
{
//clean up...
}
}
}
Обработчик исключений:
Собственно обработчик исключений имеет несколько способов регистрации, включая журнал событий, журнал файлов и, наконец, электронную почту, если все остальное не работает. Мы решили просто возвращать false, если регистратор не может выполнить ни одно из ожидаемых действий. IMO это личный выбор. Мы считаем, что вероятность того, что 3 метода не сработают последовательно (журнал событий не сработал, попробуйте файловый журнал, не сработал, попробуйте электронную почту, не сработал), очень мала. В этом случае мы решили разрешить приложению продолжить работу. Другим вариантом было бы позволить приложению полностью завершиться.
public static class MyExceptionManagement
{
public static bool LogException(Exception ex)
{
try
{
//try logging to a log source by priority,
//if it fails with all sources, return false as a last resort
//we choose not to let logging issues interfere with user experience
//if logging worked
return true;
}
catch(Exception ex)
{
//in most cases, using try-catch as a true-false is bad practice
//but when logging an exception causes an exception itself, we do
//use this as a well-considered choice.
return false;
}
}
}
Наконец, в качестве меры предосторожности мы реализовали обработчик глобального события Application_Error
(в Global.asax
). Это крайняя мера для случаев, когда мы не смогли правильно отловить ошибку. Обычно мы регистрируем и перенаправляем на дружественную страницу ошибки. Если вышеописанная обработка ошибок выполнена успешно, очень немногие ошибки попадут в глобальный обработчик.
Надеюсь, это может немного помочь. Это одно из возможных решений. Оно очень хорошо работало для нас в течение нескольких лет на некоторых больших приложениях...