Перехват исключений как ожидаемое управление потоком выполнения программы?

StackOverflow https://stackoverflow.com/questions/134698

Вопрос

Я всегда чувствовал, что ожидать, что исключения будут генерироваться регулярно, и использовать их в качестве логики потока — это плохо.Исключения кажутся такими, как будто они должны быть...исключение".Если вы ожидаете и планируете исключение, это может указывать на то, что ваш код должен быть реорганизован, по крайней мере, в .NET...
Однако.Недавний сценарий заставил меня задуматься.Я разместил это на msdn некоторое время назад, но мне бы хотелось вызвать больше дискуссий по этому поводу, и это идеальное место!

Итак, скажем, у вас есть таблица базы данных, которая имеет внешний ключ для нескольких других таблиц (в случае, который первоначально вызвал дискуссию, на нее указывали четыре внешних ключа).Вы хотите разрешить пользователю удалять данные, но только если НЕТ ссылок на внешние ключи;вы НЕ хотите выполнять каскадное удаление.
Обычно я просто проверяю, есть ли какие-либо ссылки, и если они есть, я информирую пользователя, а не удаляю.Очень легко и приятно писать, что в LINQ связанные таблицы являются членами объекта, поэтому секции.Проекты, секции.Категории и т. д. удобно вводить с помощью intellisense и все такое...
Но дело в том, что LINQ затем потенциально должен обратиться ко всем 4 таблицам, чтобы увидеть, есть ли какие-либо строки результатов, указывающие на эту запись, а обращение к базе данных, очевидно, всегда является относительно дорогостоящей операцией.

Руководитель этого проекта попросил меня изменить его, чтобы он просто перехватывал исключение SqlException с кодом 547 (ограничение внешнего ключа) и обрабатывал его таким образом.

Я был...
устойчивый.

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

На каком-то уровне это все еще кажется неправильный хотя для меня.

Что вы думаете об ожидании и намеренной обработке исключений?Ничего, если кажется, что это будет более эффективно, чем предварительная проверка?Будет ли это более сбивать с толку следующего разработчика, просматривающего ваш код, или менее сбивает с толку?Является ли это безопаснее, поскольку база данных может знать о новых ограничениях внешнего ключа, проверку которых разработчик может и не подумать?Или это вопрос точки зрения на то, что именно, по вашему мнению, является лучшей практикой?

Это было полезно?

Решение

Ух ты,

Во-первых, не могли бы вы немного разобрать вопрос? Хотя было приятно прочитать хорошо продуманный и объясненный вопрос, переварить его было довольно сложно.

Короткий ответ — «да», но это может зависеть.

  • У нас есть некоторые приложения, в которых много бизнес-логики связано с SQL-запросами (это не мой проектный директор!).Если это так устроено, руководство будет трудно убедить в обратном, поскольку это «уже работает».
  • Имеет ли это большое значение в данной ситуации?Так как это еще одна поездка через провод и обратно.Много ли делает сервер, прежде чем осознает, что не может продолжать работу (т. е. если после вашего действия происходит последовательность транзакций, она затем падает на полпути, теряя время?).
  • Имеет ли смысл сначала выполнить проверку в пользовательском интерфейсе?Поможет ли это вашему приложению?Обеспечит ли это более приятный пользовательский опыт?(т.е.Я видел случаи, когда вы выполняли несколько шагов в мастере, он запускался, а затем отключался, хотя у него была вся информация, необходимая для отказа после шага 1).
  • Является ли параллелизм проблемой?Возможно ли, что запись может быть удалена/отредактирована или что-то еще до того, как произойдет ваша фиксация (как в классическом File.Exists тьфу-тьфу).

По моему мнению:

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

Я знаю, что существует консенсус в отношении того, что исключения не следует использовать ни для чего другого, кроме исключительные обстоятельства, но помните, что здесь мы пересекаем границы приложения, ничего не ожидать.Как я уже сказал, это похоже на File.Exists, в этом нет смысла, его все равно можно удалить, прежде чем вы получите к нему доступ.

Другие советы

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

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

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

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

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

Хороший вопрос.Однако я нахожу ответы...страшный!
Исключением является своего рода GOTO.
Мне не нравится использовать исключения таким образом, потому что это приводит к спагетти-коду.
Просто как тот.

Нет ничего плохого в том, что вы делаете.Исключения не обязательно являются «исключительными».Они существуют для того, чтобы позволить вызывающим объектам иметь детальную обработку ошибок в зависимости от их потребностей.

Перехват конкретного исключения SqlException — это правильно.Это механизм, с помощью которого SQL Server сообщает состояние внешнего ключа.Даже если вы предпочитаете другой вариант использования механизма исключений, SQL Server делает это именно так.

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

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

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

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

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

Другой момент заключается в том, что такого рода исключения «короткого радиуса действия» не вызывают проблем с обслуживанием и не затрудняют чтение вашей программы.У вас будет попытка, вызов SQL для удаления и поймать все это в пределах 10 строк кода вместе.Намерение очевидно.

Это не то же самое, что выбрасывать исключение, которое должно быть перехвачено пятью вызовами процедур выше в стеке, с проблемами уверенности в том, что все объекты между ними были правильно обработаны (освобождены).

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

Мои два цента,

Ив

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top