Вот как мы подходим к проблеме:
Все вызовы с уровня 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, если регистратор не может выполнить ни одно из ожидаемых действий. ИМО, это личный выбор, хотя. Мы полагаем, что вероятность сбоя трех методов подряд (ошибка журнала событий, попытка файла журнала, ошибка, попытка электронной почты, ошибка) очень маловероятна. В этом случае мы решили позволить приложению продолжить работу. Другим вариантом будет позволить приложению полностью выйти из строя.
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
). Это последнее средство для случаев, когда мы не пытались поймать что-либо. Мы обычно регистрируем и перенаправляем на дружественную страницу ошибок. Если вышеуказанная пользовательская обработка ошибок выполнена успешно, очень мало ошибок попадет в глобальный обработчик.
Надеюсь, это немного поможет. Это одно из возможных решений. В течение нескольких лет он очень хорошо работал для нас в некоторых более крупных приложениях.