Два вопроса об управлении исключениями - PullRequest
1 голос
/ 16 февраля 2012

У меня есть пара вопросов по управлению исключениями для веб-сайта:

  • в блоке catch я могу иметь статический класс с методом дескриптора исключения, который обрабатывает исключение следующим образом:

       catch (Exception ex)
        {
            throw ExceptionHandler.HandleException(...);
        }
    

    где ExceptionHandler.HandleException - статический метод, который возвращает переменную типа System.Exception. Это хорошая практика? Любые возможные проблемы с этим подходом? Это будет потокобезопасным?

  • В моем приложении у меня есть уровень DAL, который вызывается бизнес-уровнем, а бизнес-уровень вызывается пользовательским интерфейсом. Итак, является ли хорошей практикой просто перебрасывать все пользовательские исключения, чтобы они всплыли вплоть до пользовательского интерфейса, где они отображаются, в то время как типы System.Exception регистрируются, и я выбрасываю пользовательское исключение в блоке catch? например, в DAL и Business Layer, например:

        catch (CustomExceptionBase ex)
        {
            throw;
        }
        catch (Exception sysEx)
        {
            ICustomExceptionBase ex = new SysException(sysEx);
            ex.Handle();
            throw BusinessException("Some problem while serving your request");
        }
    

    В слое пользовательского интерфейса вот так

        catch (CustomExceptionBase ex)
        {
            //when custom exception bubbles up; code to display text to user on screen
        }
        catch (Exception sysEx)
        {
            ICustomExceptionBase ex = new SysException(sysEx);
            ex.Handle();
            //display error on screen;
        }
    

    Здесь CustomExceptionBase реализует ICustomExceptionBase и наследует Exception. SysException и BusinessException оба наследуются от CustomExceptionBase.

Спасибо за ваше время ...

EDIT Цель повторного выброса в блоке system.Exceptions заключается в том, что в случае фатальной ошибки, такой как потеря соединения с базой данных или что-то подобное, я записываю ее в службу технической поддержки, возвращаю номер билета и повторно отправляю его, чтобы пользователь знал, что что-то пошло неправильно, и это ваш ссылочный номер, чтобы следить. Для всех пользовательских исключений в слое DAL или бизнес-уровне я просто раздуваю его до самого интерфейса, где отображается текст.

Ответы [ 4 ]

4 голосов
/ 16 февраля 2012

Я подозреваю, что, по крайней мере, некоторые ответы полностью зависят от вашей архитектуры. В первом случае все зависит от того, что именно делает ExceptionHandler.HandleException. Создает ли оно новое исключение на основе некоторых критериев или оно просто собирается вернуть исходное исключение?

Независимо от того, является ли он потокобезопасным или нет, полностью зависит от его реализации. Например, в следующем тривиальном случае я бы сказал, что он безопасен для потоков:

public static Exception ExceptionHandler.HandleException(Exception ex)
{
    return ex;
}

В других случаях это может быть не безопасно для потоков. например:

public static string message;
public static Exception ExceptionHandler.HandleException(Exception ex)
{
    message = ex.ToString;
    sleep(2000);
    return new Exception(message);
}

В последнем примере явно есть область, в которой переменная сообщения может быть изменена другим потоком, пока этот спит.

Что касается второго ... Исключения должны обрабатываться там, где имеет смысл обрабатывать их. Там нет жесткого и быстрого правила. Если какая-то часть кода может произвести восстановление из исключения (или хочет пропустить его), то перехватите его в этот момент, а не раньше. Если исключение действительно фатально, то ничто не должно пытаться его поймать и притвориться иначе, поэтому вы должны просто позволить ему всплыть до самого верха и сделать что-то наподобие оповещения вашего пользователя о том, что что-то не так, что вам нужно перезапустить или что-то еще.

Так что на самом деле это зависит от того, что означают ваши пользовательские исключения. Если они просто означают «Вы хотите повторить это», то это отличается от исключения, говорящего «Целостность данных была нарушена: 0 == 1». оба из них могут быть обычай, так что на самом деле вам решать, где обращаться с вещами.

1 голос
/ 16 февраля 2012

Сами по себе нет никаких технических проблем, связанных со статическим методом обработки исключений / повторных исключений, однако с точки зрения передового опыта наличие единственного метода, который волшебным образом «обрабатывает» исключения, производит впечатление потенциального запаха кода.Исключения по своему названию являются исключительными, каждый отдельный случай требует тщательной проработки, чтобы убедиться, что вы делаете правильные вещи, и поэтому я считаю маловероятным, что ваш HandleException метод всегда будет делать что-то разумное.

В качестве крайнего примера я знаю одно такое приложение, в котором почти каждый отдельный метод был обернут в блок try-catch с вызовом статического метода-обработчика исключений, который генерировал общий тип MyApplicationException.Это очень плохая идея:

  • Это загромождает код
  • Это усложняет понимание трассировки стека
  • Это очень затрудняет для вызывающих абонентовловить и обрабатывать определенные типы исключений
  • Это делает исключение еще более значительным, чем раньше

Моим любимым был метод, который не был реализован и который выглядел примерно так:

void SomeException()
{
    try
    {
        throw new NotImplementedException();
    }
    catch(Exception ex)
    {
        throw ExceptionHandler.HandleException(...);
    }
}

Хуже всего, конечно, то, что он совершенно бессмысленный .Как я уже говорил, исключения являются исключительными - каждый блок try ... catch требует тщательного обдумывания и рассмотрения того, как он должен вести себя, использование общего метода HandleException является сигналом немедленного предупреждения, что, вероятно, это не так.

Возврат исключений

В общем случае исключать исключение следует только в двух случаях:

  • Если вы хотите добавить контекстную информацию к исключению (например, имятекущего обрабатываемого файла)
  • Когда вам нужно было перехватить исключение, чтобы обработать какой-то конкретный случай, например, обработка ошибки «недостаточно места на диске»

    catch (IOException ex)
    {
        long win32ErrorCode = Marshal.GetHRForException(ex) & 0xFFFF;
        if (win32ErrorCode == ERROR_HANDLE_DISK_FULL || win32ErrorCode == ERROR_DISK_FULL)
        {
            // Specific "out of disk space" error handling code
        }
        else
        {
            throw;
        }
    }
    

«Пузырьки» (то есть перехват и перебрасывание исключения без каких-либо действий с ним) совершенно не нужны - это то, что исключения уже разработаны, чтобы делать все сами!

Обработка исключений

Другие люди говорили, что "исключения должны обрабатываться там, где это имеет смысл", и я даже дал этоСам, но, оглядываясь назад, не думаю, что это особенно полезный совет!:)

Под этим обычно подразумевают, что вы должны обрабатывать исключения по особым причинам , и что вам следует выбирать, где в вашем приложении обрабатывать это исключение в зависимости от этой причины.

Например, если вы хотите отобразить сообщение об ошибке, информирующее пользователя о том, что у него нет разрешения на изменение файла, если вы получаете сообщение об ошибке отказа в доступе, возможно, в вашем коде пользовательского интерфейса есть определенный блок try-catchчто делает это:

catch (IOException ex)
{
    long win32ErrorCode = Marshal.GetHRForException(ex) & 0xFFFF;
    if (win32ErrorCode == ERROR_ACCESS_DENIED)
    {
        // Display "access denied error"
    }
    else
    {
        throw;
    }
}

Обратите внимание, что это очень специфично для этого случая, который мы хотим обработать - он перехватывает только тот конкретный тип исключения, в котором заинтересованы , а выполняет дополнительные проверкичтобы отфильтровать до интересующего нас конкретного случая.

В качестве альтернативы, если вы хотите регистрировать необработанные ошибки или корректно отображать сообщения об ошибках для пользователя вместо экрана ошибок IIS 505, то место для этого либо вGlobal.asax или через пользовательскую страницу ошибок - ASP.Net Страницы пользовательских ошибок

Моя точка зрения заключается в том, что при обработке исключений мы тщательно обдумываем, чего мы хотим достичь с точки зрения функциональности приложения (например, логики повторов, сообщений об ошибках, ведения журнала и т. Д.) И реализации нашей логики обработки исключений.чтобы конкретно удовлетворить эти требования самым целенаправленным способом - не существует волшебной структуры обработки исключений и нет стандартного кода.

Избегайте исключений полностью, когда это возможно!

Я обычно нахожу, что лучшая стратегия - просто избегать исключений, когда это возможно! Например, если ваша страница анализирует вводимые пользователем числа, и вы хотите отображать сообщения проверки, когда они вводят глупые значения, то проверяйте ввод заранее, вместо того, чтобы перехватывать исключения:

Bad:

void DoSomething()
{
    int age = int.Parse(ageTextBox.Text);
    if (age < 0)
    {
        throw new ArgumentOutOfRangeException("age must be positive");
    }
    if (age >= 1000)
    {
        throw new ArgumentOutOfRangeException("age must be less than 1000");
    }
}

void Button_Click(object sender, EventArgs e)
{
    try
    {
        DoSomething();
    }
    catch (Exception ex)
    {
        DisplayError(ex.Message);
    }
}

Хорошо:

void Button_Click(object sender, EventArgs e)
{
    int age;
    if (!int.TryParse(ageTextBox.Text, out age))
    {
        DisplayError("Invalid age entered");
    }
    if (age < 0)
    {
        DisplayError("age must be positive");
    }
    if (age >= 1000)
    {
        DisplayError("age must be less than 1000");
    }

    DoSomething();
}

Пользователи постоянно вводят неверные данные - обработка это действительно логика приложения и не должна попадать в реальность обработки исключений - это, безусловно, не событие, которое я бы назвал «исключительным».

Конечно, это не всегда возможно, однако я считаю, что использование этой стратегии упрощает код и облегчает следование логике приложения.

1 голос
/ 16 февраля 2012

Да, вы можете вызывать обработчик статических исключений внутри блока catch, и он, вероятно, будет потокобезопасным, если вы не ссылаетесь на статические переменные.

Вам стоит взглянуть на корпоративную библиотеку Microsoft. Он имеет почти такой же дизайн, но использует политики исключений, определенные в файле web.config, для управления тем, как исключение всплывает, переносится или отбрасывается. Соедините это с блоком регистрации приложений, и вы получите полное решение.

0 голосов
/ 16 февраля 2012

Прежде всего нам необходимо рассмотреть типы исключений. В любом бизнес-исключении может быть либо BusinessException, либо Technical / SystemException.

  1. В BusinessException мы можем отправлять пользовательские исключения с подробными сведениями об ошибках.

  2. В Техническом / Системном Исключении мы не должны обрабатывать его, а позволить ему всплыть на уровне пользовательского интерфейса. Я могу решить, какая ошибка должна отображаться в случае исключений.

  3. В ваших подходах, если вы обрабатываете исключение или выбрасываете пользовательское исключение, трассировка вызова теряется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...