如果有这么多事情可能会出错,你所做的就是试试,试试吧

luk*_*m00 11 c# exception-handling

说真的,你如何处理所有这些例外而不必坚持?我是否读过太多关于异常处理的文章或者什么?我尝试过几次重构,每次我最终都会遇到更糟糕的事情.也许我应该承认异常确实发生了,只是喜欢编码只是快乐的道路?;)那么这段代码有什么问题(除了我懒得扔掉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);
    }
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句.很多次我在形成问题时努力做到尽可能具体 - 结果是没有访问,没有答案,也不知道如何解决问题.这次我想到了别人的问题引起我注意的所有时间,猜猜这是正确的做法:)

更新:

我已经尝试将一些技巧付诸实践,这就是我到目前为止所提出的.我决定稍微改变行为:当它是不可能的设置信息状态成功后出口到成品我认为这是工作没有完全完成,我回滚并抛出一个异常.如果你们还有一些耐心,请让我知道它是否更好.或者在我一些更多的批评.顺便说一句.感谢所有的答案,我分析了每一个答案.

抛出一个System.Exception不正确的实例,所以我按照建议摆脱了这一点,而是决定引入一个自定义异常.顺便说一句,这似乎也不对 - 矫枉过正?这似乎是罚款与公共方法,但有点过度设计的私有成员,但我还是想知道有与数据库连接或什么改变消息状态,而不是问题的问题.

我能看到几个在这里提取方法的方式,但他们都似乎混合其职责jgauffin中提到他的评论:管理数据库连接,处理数据库操作,业务逻辑(出口数据).说的ChangeStatus方法-它的某种抽象级别-您更改消息的状态,你不感兴趣,这件事情是如何发生的,消息是怎么坚持,等等也许我应该使用数据映射模式,以进一步单独责任,但在这个仍然非常简单的场景中,我以为我会使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

jga*_*fin 14

  1. 数据集是所有邪恶的根源;)尝试使用ORM代替.
  2. 阅读单一责任原则.你的代码做了很多不同的事情.
  3. 不要仅仅为了重新抛出它们而捕获异常
  4. 在事务和连接上使用using语句.
  5. 当所有异常处理程序执行相同操作时,无需捕获所有不同的异常.异常详细信息(异常类型和消息属性)将提供信息.

  • @ 4)我也应该在事务中使用`using`吗?处理连接是不是足够了?或者,如果你的意思是限制try/catch块的数量,那么当我无法捕获异常时,我将如何知道何时回滚事务?此外,我在捕获异常时使用过滤器习惯用法能够记录异常的特定数据,但如果有任何其他*整洁*方式(不是口袋妖怪例外)这样做,那么我会非常高兴摆脱其中一些捕获块. (2认同)
  • @ 3)总是尝试只处理真正需要由方法处理的异常.不要害怕让异常通过调用堆栈传播.您将获得更清晰的代码. (2认同)

Ste*_*ger 9

除了jgauffin伟大答案.

6.不要仅仅为了记录它们而捕获异常.抓住他们在最顶层并记录那里的所有例外.

编辑:

因为异地日志记录至少具有以下缺点:

  1. 可能会多次记录相同的异常,这会不必要地填充日志.很难说实际发生了多少错误.
  2. 如果调用者捕获并处理或忽略异常,则日志文件中仍然存在错误消息,这至少令人困惑.