【问题标题】:Modify Exception and throw it修改异常并抛出
【发布时间】:2025-12-14 01:45:02
【问题描述】:

我想用附加信息修改Exception 中的Message 属性。例如从EF生成的SQL

但我不想丢失原始Exception 的任何内容。这会让我失去stacktrace

catch (Exception ex)
{
    throw ex;
}

这些Exception 来自数据层。我想throw他们,以便他们可以用Elmah记录。

我有什么选择?

【问题讨论】:

  • 顺便说一句(我认为@LB2 在他/她的回答中暗示了这一点),您应该使用throw; 而不是throw ex;,因为throw ex; 甚至不会保留原始堆栈跟踪你的错误。

标签: c# exception elmah.mvc


【解决方案1】:

如果你想添加一些东西,你可以把它包装在另一个异常中:

catch( Exception ex)
{
   throw new Exception("my new message",ex);
}

您将能够使用完整的堆栈跟踪访问内部异常

【讨论】:

  • 虽然这可行,但这是一个糟糕的建议。您不仅会丢失抛出异常的上下文,而且会鼓励用户捕获一般异常,这几乎总是一个坏主意
  • 如果将上下文包装为内部异常,为什么会丢失上下文?由于问题是 - 如何保持上下文并创建新消息,它回答了这个问题。我完全同意您应该捕获特定异常(并非总是如此)并且您应该创建自定义异常来抛出(并非总是如此),但即使我坚信您也应该回答您被问到的问题......
  • 您将失去上下文,因为实际异常现在是内部异常,而您的新异常是一般异常 - 所以如果您想处理这个特定异常,您需要捕获 Exception 和然后检查内部异常(这是混乱的)。正确的解决方案是派生自己的异常并抛出它。就像我说的那样,您的答案会奏效,但这绝对不是处理这种特殊情况的最佳方式。
【解决方案2】:

定义你的自定义异常类,把原来的异常作为内部异常,抛出被包裹的异常:

public class CustomException : Exception
{
    public CustomException()
        : base()
    {
    }

    public CustomException(string message)
        : base(message) 
    { 
    }

    public CustomException(string message, Exception innerException)
        : base(message, innerException)
    { 
    }

//...other constructors with parametrized messages for localization if needed
}

catch (Exception ex)
{
    throw new CustomException("Something went wrong", ex);
}

当然它的名字应该相应地改变。这样,您就可以完全控制域中使用的异常,并且不会丢失原始 throw 中的任何信息。

具有良好类名的自定义异常非常有用,尤其是在大型项目中。它们有助于在许多简单情况下从一开始就诊断问题,而无需阅读异常的详细信息和调试。只有当一些真正复杂的问题发生时才需要后者。因此,将裸露的 Exception 实例相互包裹起来似乎是一种不好的做法。

【讨论】:

    【解决方案3】:

    您的问题的简短回答是,您不能。异常实际上是故障报告,因此代表历史事件。历史事件本质上是不可变的,因此您不能(也不应该想要)“修改”它们。

    相反,您可以通过捕获异常并抛出 new 异常来为异常添加一些上下文。这将允许您附加额外的诊断信息并传入原始异常,从而维护整个故障跟踪。

    我不会详细介绍如何编写自定义/使用异常,因为我之前已经在此主题上写过 detailed answer。然而,作为一个例子,你的处理代码最终应该看起来像

    catch (SomeEntityFrameworkException ex)
    {
        throw new MyCustomException("Some additional info", ex);
    }
    

    【讨论】:

      【解决方案4】:

      我只会创建自己的异常,其中包含原始异常和您想要的任何其他信息,在您的 catch 块中创建新异常并抛出它

      【讨论】:

        【解决方案5】:

        只要做:

        catch (Exception ex)
        {
            throw;
        }
        

        这会进一步传递异常而不重新抛出它,并保留上下文。

        【讨论】:

        • 这将如何向ex.Message 属性添加其他信息?
        • 原始代码示例遗漏了它,我也是。如果它是自定义异常,我认为 OP 想要修改的属性是读/写的。如果异常是只读的,则无法完成。我唯一回答的是如何在不丢失原始捕获的堆栈和其他上下文的情况下重新抛出异常。