Плохая форма для использования try / catch в качестве теста? - PullRequest
3 голосов
/ 08 марта 2011

У меня такое ощущение, что это так, но я хотел подтвердить - это плохая форма, чтобы делать что-то вроде:

try
{
    SqlUpload(table);
}
catch(PrimaryKeyException pke)
{   
          DeleteDuplicatesInTable(table);
          SqlUpload(table);
}
catch(Exception ex)
{
    Console.Write(ex);
}

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

Я прошу прощения за грубую природу этого примера, но это было просто для иллюстрации.

Ответы [ 7 ]

3 голосов
/ 08 марта 2011

Исключения могут быть правильно использованы в управлении транзакциями, но в данном примере это не так.На мой первый взгляд оказалось, что это похоже на то, что делает класс * DataContext в Linq2Sql при вызове SubmitChanges().Однако эта аналогия была неверной.(Пожалуйста, смотрите комментарий Криса Марисика к моему посту для точной критики сравнения).

Об исключениях

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

Выдержка из DataContext.SubmitChanges

Этот код показывает пример правильное использование исключений в управлении транзакциями .

Примечание. То, что Microsoft делает это, автоматически не означает ее право.Тем не менее, их код имеет довольно хороший послужной список.

      DbTransaction transaction = null;
        try
        {
            try
            {
                if (this.provider.Connection.State == ConnectionState.Open)
                {
                    this.provider.ClearConnection();
                }
                if (this.provider.Connection.State == ConnectionState.Closed)
                {
                    this.provider.Connection.Open();
                    flag = true;
                }
                transaction = this.provider.Connection.BeginTransaction(IsolationLevel.ReadCommitted);
                this.provider.Transaction = transaction;
                new ChangeProcessor(this.services, this).SubmitChanges(failureMode);
                this.AcceptChanges();
                this.provider.ClearConnection();
                transaction.Commit();
            }
            catch
            {
                if (transaction != null)
                {
                    try
                    {
                        transaction.Rollback();
                    }
                    catch
                    {
                    }
                }
                throw;
            }
            return;
        }
        finally
        {
            this.provider.Transaction = null;
            if (flag)
            {
                this.provider.Connection.Close();
            }
        }
    }
1 голос
/ 08 марта 2011

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

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

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

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

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

Во-вторых, библиотека может не позволить вамсделай выбор.Например, int.TryParse является хорошей альтернативой int.Parse, если входные данные еще не проверены.С другой стороны, библиотека может не иметь опцию выбрасывания исключений.В этом случае не собирайте свой собственный код для проверки без исключений - хотя может быть плохой способ использовать обработку исключений для проверки на ожидаемое состояние, его худшая форма - дублировать функциональность библиотеки, просто чтобы избежатьисключение.Просто используйте try/catch и, возможно, добавьте небольшой комментарий о том, что вы НЕ ХОТИТЕ это сделать, но авторы библиотеки СДЕЛАЛИ вас :).

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

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

1 голос
/ 08 марта 2011

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

1 голос
/ 08 марта 2011

Да, этот тип кода считается плохой формой в .NET.

Вам лучше написать любой код, подобный

if(HasPrimaryKeyViolations(table))
    DeletePrimaryKeyViolations(table)

SqlUpload(table)
0 голосов
/ 08 марта 2011

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

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

0 голосов
/ 08 марта 2011

Одна проблема заключается в том, что любое исключение, вызванное вызовом SqlUpload () в блоке catch, вызывает сбой приложения, поскольку дальнейшая обработка исключений отсутствует.

0 голосов
/ 08 марта 2011

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

...