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

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


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

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

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

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


На каком-то уровне мне все еще кажется неловко.

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

Ответ 1

Ничего себе,

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

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

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

По-моему:

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

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

Ответ 2

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

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

То, что вы НЕ должны делать, - это улов и подавление исключений с помощью обложки catchall. В первую очередь исключение исключений было разработано в первую очередь.

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

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

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

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

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

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

Ив