Когда так много всего может пойти не так, все, что вы делаете, это пытайтесь, пытайтесь, пытайтесь - PullRequest
11 голосов
/ 30 июня 2010

Серьезно, как вы можете обрабатывать все эти исключения, не сходя с ума?Я прочитал слишком много статей об обработке исключений или что?Я пробовал рефакторинг пару раз, и каждый раз, кажется, что-то получалось еще хуже.Может быть, я должен признать, что исключения случаются, и просто наслаждаться кодированием просто happy path ?;) Так что же не так с этим кодом (кроме того, что мне лень было просто выбросить Exception вместо чего-то более конкретного)?И во что бы то ни стало, не беспокойся обо мне.

public void Export(Database dstDb)
{
    try
    {
        using (DbConnection connection = dstDb.CreateConnection())
        {
            connection.Open();
            DbTransaction transaction = connection.BeginTransaction();
            try
            {
                // Export all data here (insert into dstDb)
                transaction.Commit();
            }
            catch (SqlException sqlex)
            {
                ExceptionHelper.LogException(sqlex);
                try
                {
                    transaction.Rollback();
                }
                catch (Exception rollbackEx)
                {
                    logger.Error("An exception of type " + rollbackEx.GetType() +
                                      " was encountered while attempting to roll back the transaction.");
                }
                throw new Exception("Error exporting message " + Type + " #" + Id + ": [" + sqlex.GetType() + "] " + sqlex.Message, sqlex);
            }
            catch (Exception ex)
            {
                try
                {
                    transaction.Rollback();
                }
                catch (Exception rollbackEx)
                {
                    logger.Error("An exception of type " + rollbackEx.GetType() +
                                      " was encountered while attempting to roll back the transaction.");
                }
                throw new Exception("Error exporting message " + Type + " #" + Id + ": [" + ex.GetType() + "] " + ex.Message, ex);
            }
        }

        try
        {
            Status = MessageStatus.FINISHED;
            srcDb.UpdateDataSet(drHeader.Table.DataSet, HEADERS,
                CreateHeaderInsertCommand(), CreateHeaderUpdateCommand(), null);
        }
        catch (Exception statusEx)
        {
            logger.ErrorException("Failed to change message status to FINISHED: " +
                                    Type + " #" + Id + ": " + statusEx.Message, statusEx);
        }
    }
    catch (Exception importEx)
    {
        try
        {
            Status = MessageStatus.ERROR;
            srcDb.UpdateDataSet(drHeader.Table.DataSet, HEADERS,
                    CreateHeaderInsertCommand(), CreateHeaderUpdateCommand(), null);
        }
        catch (Exception statusEx)
        {
            logger.ErrorException("Failed to change message status to ERROR: " +
                                    Type + " #" + Id + ": " + statusEx.Message, statusEx);
        }
        AddErrorDescription(importEx.Message);
        throw new Exception("Couldn't export message " + Type + " #" + Id + ", exception: " + importEx.Message, importEx);
    }
}

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

Обновление:

Я пыталсяприменение некоторых советов на практике, и вот что я придумал до сих пор.Я решил немного изменить поведение: когда после успешного экспорта невозможно установить статус сообщения на ЗАВЕРШЕНО, я рассматриваю это как работу, выполненную не полностью, и откатываю и выкидываю исключение.Если у вас, ребята, осталось немного терпения, пожалуйста, дайте мне знать, если это будет лучше.Или бросить еще немного критики на меня.Btw.Спасибо за все ответы, я анализирую каждый из них.

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

IЗдесь можно увидеть несколько способов извлечения методов, но все они, кажется, смешивают обязанности, которые jgauffin упоминали в его комментарии : управление подключением к базе данных, обработка операций с базой данных, бизнес-логика (экспорт данных).Скажем, метод ChangeStatus - это какой-то уровень абстракции - вы меняете статус сообщения, и вам не интересно, как это происходит, как сохраняется сообщение и т. Д. Возможно, мне следует использовать шаблон Data Mapper для дальнейшегоотдельные обязанности, но в этом все еще довольно простом сценарии я думал, что сойдет с рук Active Record.Может быть, весь дизайн настолько запутан, что я не вижу, где делать надрезы?

public void Export(Database dstDb)
{
    try
    {
        using (DbConnection connection = dstDb.CreateConnection())
        {
            connection.Open();
            using (DbTransaction transaction = connection.BeginTransaction())
            {
                // Export all data here (insert into dstDb)
                ChangeStatus(MessageStatus.FINISHED);
                transaction.Commit();
            }
        }
    }
    catch (Exception exportEx)
    {
        try
        {
            ChangeStatus(MessageStatus.ERROR);
            AddErrorDescription(exportEx.Message);
        }
        catch (Exception statusEx)
        {
            throw new MessageException("Couldn't export message and set its status to ERROR: " +
                    exportExt.Message + "; " + statusEx.Message, Type, Id, statusEx);
        }
        throw new MessageException("Couldn't export message, exception: " + exportEx.Message, Type, Id, exportEx);
    }
}

private void ChangeStatus(MessageStatus status)
{
    try
    {
        Status = status;
        srcDb.UpdateDataSet(drHeader.Table.DataSet, HEADERS,
            CreateHeaderInsertCommand(), CreateHeaderUpdateCommand(), null);
    }
    catch (Exception statusEx)
    {
        throw new MessageException("Failed to change message status to " + status + ":" + statusEx.Message, statusEx);
    }
}

Ответы [ 4 ]

14 голосов
/ 30 июня 2010
  1. Наборы данных являются корнем всего зла;) Попробуйте вместо этого использовать ORM.
  2. Читайте о принцип единоличной ответственности . ваш код делает много разных вещей.
  3. Не отлавливать исключения только для их отбрасывания
  4. Используйте , используя оператор для транзакции и соединения.
  5. Нет необходимости перехватывать все разные исключения, когда все обработчики исключений делают одно и то же. Сведения об исключении (тип исключения и свойство сообщения) предоставят информацию.
9 голосов
/ 30 июня 2010

В дополнение к великолепному ответу jgauffin .

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

Редактировать:

Поскольку регистрация исключений повсюду имеет как минимум следующие недостатки:

  1. Одно и то же исключение может регистрироваться несколько раз, что наполняет журнал без необходимости.Трудно сказать, сколько ошибок действительно произошло.
  2. Если исключение поймано и обработано или проигнорировано вызывающим абонентом, в файле журнала все еще есть сообщения об ошибках, что по крайней мере сбивает с толку.
4 голосов
/ 30 июня 2010

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

  1. Никогда не выдавайте System.Exception, обычно существует достаточно типов исключений, чтобы удовлетворить ваше требование, если нет, специализировать.См .: http://www.fidelitydesign.net/?p=45
  2. Вызывать исключение только в том случае, если сам метод ничего не может сделать, кроме как выдать исключение.Если метод может восстановить / обработать ожидаемые изменения ввода / поведения, то не выбрасывайте исключение.Создание исключений является ресурсоемким процессом.
  3. Никогда не отлавливайте исключение только для его повторного выброса.Ловите и перебрасывайте, если вам нужно выполнить какую-то дополнительную работу, например, сообщить об исключении или обернуть исключение в другое исключение (обычно я делаю это для работы WCF или транзакции).

Это то, что я делаю лично, многие разработчики предпочитают делать это так, как им удобно ...

0 голосов
/ 30 июня 2010

Создайте класс журнала, который обрабатывает запасные варианты для собственных сбоев (т. Е. Пытается SQL, если не удается записать в журнал событий, если это не удается, пишет в локальный файл журнала и т. Д.).

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

public void Export(Database dstDb)
{
  try
  {
    using (DbConnection connection = dstDb.CreateConnection())
    {
        connection.Open();
        using (DbTransaction transaction = connection.BeginTransaction())
        {
            // Export all data here (insert into dstDb)
            ChangeStatus(MessageStatus.FINISHED);
            transaction.Commit();
        }
    }
  }
  catch (Exception exportEx)
  {
    LogError(exportEx);// create a log class for cross-cutting concerns 
        ChangeStatus(MessageStatus.ERROR);
        AddErrorDescription(exportEx.Message);

    throw; // throw preserves original call stack for debugging/logging
  }
}

private void ChangeStatus(MessageStatus status)
{
  try
  {
    Status = status;
    srcDb.UpdateDataSet(drHeader.Table.DataSet, HEADERS,
        CreateHeaderInsertCommand(), CreateHeaderUpdateCommand(), null);
  }
  catch (Exception statusEx)
  {
   Log.Error(statusEx);
   throw;
  }
}

Также для любой ситуации, когда вы чувствуете, что необходимы дополнительные блоки try / catch, сделайте их собственным методом, еслиони слишком уродливы.Мне нравится ответ Стефана Штайнеггера, где вызов верхнего уровня в вашем приложении - лучшее место для отслеживания.

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

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