Подтвердить что ты не робот

Когда так много вещей может пойти не так, как надо, попробуйте, попробуйте, попробуйте

Серьезно, как вы можете справиться со всеми этими исключениями, не обойдясь? Прочитал ли я слишком много статей об обработке исключений или о чем? Я пробовал рефакторинг это несколько раз, и каждый раз, когда я, кажется, получаю что-то еще хуже. Может быть, я должен признать, что исключения случаются и просто пользуются кодировкой только счастливого пути?;) Так что же не так с этим кодом (кроме того, что я был достаточно ленив просто бросить 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. Так много раз я очень старался быть настолько конкретным, насколько это возможно при формировании вопросов - в результате не было ни посещений, ни ответов, ни идеи, как решить проблему. На этот раз я думал обо всех случаях, когда кто-то еще интересовался моим вниманием, думаю, это было правильное дело:)

Update:

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

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

Я вижу пару способов извлечения методов здесь, но все они, похоже, смешивают обязанности, которые 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);
    }
}
4b9b3361

Ответ 1

  • Наборы данных являются корнем всего зла;) Вместо этого используйте ORM.
  • Читайте о принцип единой ответственности. ваш код делает много разных вещей.
  • Не перехватывайте исключения только для их реорганизации.
  • Используйте с помощью оператора транзакции и подключения.
  • Не нужно улавливать все разные исключения, когда все обработчики исключений выполняют одно и то же. Сведения об исключении (тип исключения и свойство сообщения) будут содержать информацию.

Ответ 2

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

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

Изменить:

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

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

Ответ 3

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

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

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

Ответ 4

Создайте класс журнала, который обрабатывает откаты для собственных сбоев (т.е. пытается 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, сделайте их своим методом, если они слишком уродливы. Мне нравится ответ Stefan Steinegger, где лучший вызов в вашем приложении - лучшее место для улавливания.

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