Обработка ошибок Должен ли я генерировать исключение? Или обращаться с источником? - PullRequest
13 голосов
/ 30 августа 2009

У меня есть такой формат

asp.net MVC View -> Сервисный уровень -> Репозиторий.

Таким образом, представление вызывает уровень обслуживания, в котором есть логика ведения бизнеса / проверки, которая, в свою очередь, вызывает хранилище.

Теперь мой метод сервисного уровня обычно имеет тип возврата bool, так что я могу вернуть true, если запрос к базе данных прошел успешно. Или если это не удалось. Затем пользователю показывается общее сообщение.

Я, конечно, запишу ошибку в elmah. Однако я не уверен, как мне добраться до этого момента.

Как и сейчас, мой репозиторий имеет пустые типы возврата для обновления, создания, удаления.

Так, скажем, если обновление завершится неудачно, если у меня в репозитории будет попытка / перехват, который выдает ошибку, то мой уровень обслуживания перехватывает ее и отправляет сигнал elmah и возвращает false?

Или мне нужно, чтобы эти методы хранилища возвращали «bool», пытались / ловили ошибку в хранилище, а затем возвращали «true» или «false» на сервисный уровень, что, в свою очередь, возвращает «true» или «false» для вид?

Обработка исключений все еще сбивает меня с толку, как обрабатывать ошибки и когда генерировать, и когда отлавливать ошибку.

Ответы [ 4 ]

20 голосов
/ 30 августа 2009

Эмпирическое правило, которое я всегда использую:

  • На низких уровнях бросать, если операция не может быть завершена из-за исключительных обстоятельств.
  • В средних слоях перехватывайте несколько типов исключений и переворачивайте в один тип исключений.
  • Обработка исключений в последний ответственный момент.
  • ДОКУМЕНТ!

Вот пример псевдокода для многослойного приложения ASP.NET MVC (пользовательский интерфейс, контроллер, логика, безопасность, репозиторий):

  1. Пользователь нажимает кнопку отправки.
  2. Действие контроллера выполняется и вызывает уровень логики (бизнеса).
  3. Вызов метода логики в Security с текущими учетными данными пользователя
    • Пользователь недействителен
      • Слой безопасности создает исключение SecurityException
      • Слой логики ловит, оборачивает в LogicException с более общим сообщением об ошибке
      • Контроллер ловит LogicException, перенаправляет на страницу ошибок.
    • Пользователь действителен и Security возвращает
  4. Слой логики вызывает хранилище для завершения действия
    • Ошибка хранилища
      • Репозиторий создает исключение RepositoryException
      • Слой логики ловит, оборачивает в LogicException с более общим сообщением об ошибке
      • Контроллер ловит LogicException, перенаправляет на страницу ошибок.
    • Хранилище успешно
  5. Логический слой возвращает
  6. Контроллер перенаправляет в представление «Успех».

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

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


Кстати, ничего не говорит, что вы не можете сделать это:

try
{
  var result = DoSomethingOhMyWhatIsTheReturnType();
}
catch(LogicException e)
{
  if(e.InnerException is SqlException)
  {
    // handle sql exceptions
  }else if(e.InnerException is InvalidCastException)
  {
    // handle cast exceptions
  }
  // blah blah blah
}
2 голосов
/ 30 августа 2009

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

Не злоупотребляйте исключениями для простого управления потоком - это было бы глупее всего.

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

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

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

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

Однако исключения, с которыми вы не можете по-настоящему справиться (что вы хотите сделать с «исключением OutOfMemory», действительно?), Должны быть оставлены нетронутыми - может быть, вызывающий абонент дальше по стеку вызовов может справиться с этим - или нет. Марк

1 голос
/ 30 августа 2009

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

Рассмотрим эти два API:

int int.Parse(string integerValue); // In this case, the method will return int
                                    // or it will die! That means your data must be
                                    // valid for this method to function.

bool int.TryParse(string integerValue, out number); // In this case, we expect the data
                                                    // we passed in might not be fully
                                                    // valid, hence a boolean.
0 голосов
/ 30 августа 2009

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

Как правило, вы хотите использовать исключения для исключительных случаев (исключения влекут за собой снижение производительности, поэтому чрезмерное их использование, особенно в «запутанных» ситуациях, может иметь решающее значение). Допустим, по какой-то причине хранилище не может подключиться к серверу базы данных. Тогда вы бы использовали исключение. Но если в хранилище выполняется поиск какого-либо объекта по идентификатору, а объект не найден, вы должны вернуть null вместо того, чтобы выдавать исключение о том, что объект с идентификатором x не существует.

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

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

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

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