n-слой, используя попытку поймать
Я работаю над n-уровневым приложением и выполняю try/catch только на своем уровне презентации. Если ошибка возникает на уровне данных или бизнес-уровне, эта ошибка фиксируется при попытке / перехвате в моем уровне представления. Это хорошо, или я должен работать с try catch в каждом методе каждого слоя?
2 ответа
Вот что вы хотите избежать на любом уровне: набор методов, которые выглядят так:
void method()
{
try
{
// some code here that may potentially throw an exception
}
catch ( /* anything here */)
{
// code right here to handle that exception
}
}
Если это то, что вы делаете, вы можете также вернуться к старому VB On Error Goto
система, потому что вы ничего не получили. Исключения обеспечивают два основных преимущества для обработки ошибок: способность легко обрабатывать различные типы ошибок по-разному и возможность для ошибок, которые могут быть захвачены дальше в стеке вызовов программы. Это второе преимущество, о котором вы спрашиваете здесь.
Итак, мы видим, что вы хотите разрешить исключениям "пузыриться" на более высокие уровни, так как это большая часть того, почему у нас вообще есть исключения... но вы всегда хотите обрабатывать их на уровне представления? Нет, мы можем сделать лучше. Иногда могут существовать бизнес-правила о том, как реагировать на определенные исключения из уровня данных. Иногда сам уровень данных может обрабатывать и восстанавливаться после исключения, не предупреждая уровень над ним.
С другой стороны, преимущество исключений состоит в том, что это может позволить вам писать более простой код на нижних уровнях с меньшими перерывами от нормального потока выполнения программы для кода обработки ошибок. Это достигается ценой размещения большего количества try / catch на уровне презентации. Опять же, это не означает, что уровень презентации - это единственное место, где можно с ними справиться, но это место, где нужно приложить усилия, чтобы убедиться, что они не пройдут ваш уровень презентации незамеченными. Если вы не можете справиться с ними где-либо еще, имейте способ поймать их на уровне представления и показать их пользователю дружественным способом. Также рекомендуется использовать один и тот же механизм для регистрации или отчета об исключениях, чтобы вы могли получить хорошие метрики на предмет сбоев ваших приложений, а затем использовать эту информацию, чтобы сделать ваше приложение лучше.
Когда вы дойдете до того, что находитесь внутри обработчика исключений последней очереди, вы также можете рассмотреть вопрос о прекращении работы приложения. Если у вас действительно происходят непредвиденные события, такие как необработанные исключения, которые проходят через уровень представления, есть обоснованная точка зрения, согласно которой, возможно, будет не очень хорошая идея продолжать запускать программу. Но даже в этом случае вы захотите отловить и попытаться сообщить об исключении, а затем завершить работу как можно более изящно.
В общем, лучше поймать исключение как можно ближе к тому месту, где оно может позволить вашему коду потенциально сделать что-то, чтобы исправить / адаптировать / отреагировать на проблему. Что это "делать что-то" зависит от обстоятельств. Например, если у вас произошел сбой вызова уровня сервиса, вы можете повторить вызов, потому что сервис был слишком занят; тогда как если ваша хранимая процедура не работает, то не имеет значения, сколько раз вы пытаетесь повторить ее, она будет нарушена до тех пор, пока логика не будет исправлена в базе данных.
Если все, что вы хотите сделать, это регистрировать ошибки, то обнаружение ошибки как можно ближе к месту ее возникновения является менее полезным.
Каждый проект, над которым я когда-либо работал, имел try-catch
блоки в каждом слое приложения.
Следствие к try-catch
Это концепция Fail Fast, которая обычно говорит о том, что производительность отладки увеличивается, когда система сразу выходит из строя, а не медленно выходит из строя (читай: по прошествии часов, дней, недель, месяцев или даже лет работы).
Хорошим примером быстрого сбоя в.NET Framework является использование Convert.ToInt32()
по сравнению с прямой бросок с использованием (int)
, как это:
int? settingValue = Convert.ToInt32(SomeSettingString);
if(settingValue == null)
{
// Do something here
}
else
{
// Do something else here
}
Если SomeSettingString
может быть преобразован в int
тогда значение устанавливается на и Do something else
логика выполнена. Предположим, через год настройки меняются и null
возвращается, потому что преобразование не удается, теперь внезапно Do something here
Логика выполняется, и это отладочное приключение, чтобы выяснить, что такое условие происходит, если вы вообще можете это выяснить. Кажется, что большинство подобных проблем происходит только в PRODUCTION, а не в DEV.
Теперь давайте посмотрим на то же самое, но быстро провалившись, вот так:
try
{
int settingValue = (int)SomeSettingString;
}
catch(Exception ex)
{
// Fail fast and throw exception
throw new Exception("Fail fast");
}
Теперь исключение происходит немедленно, когда строка настройки вызывает преобразование в int
отказ.
Примечание: остерегайтесь того, что неудачный пост может быть саботирован пустым catch
блоки, которые "едят" исключения. try
блоки с пустыми catch
блоков следует избегать, потому что они неизменно приводят к "съеденному" сценарию исключения.
Не делай этого:
try
{
// Exception waiting to happen here
}
catch(Exception ex)
{
// Catch-all, because all exceptions derive from Exception class
// So this will eat exceptions and pretend like they never happened
}