如何在InvariantCulture中记录异常?

Vla*_*tov 0 c# error-logging exception-handling internationalization asp.net-mvc-4

在我的MVC解决方案中,我有两个处理程序用于例外.首先记录Logs数据库的异常:

public sealed class LogErrorAttribute : Attribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        // some handling with filterContext.Exception
    }
}
Run Code Online (Sandbox Code Playgroud)

第二个显示用户异常:

public sealed class AsyncAwareHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
         // some handling with filterContext.Exception    
    }
}
Run Code Online (Sandbox Code Playgroud)

当发生任何未处理的异常时,它们都会被触发:

throw new ArgumentNullException("email", i18n.someErrorMessage);
Run Code Online (Sandbox Code Playgroud)

i18n.someErrorMessage是从resx文件中翻译的字符串.

在两个处理程序中filterContext.Exception.Message我都有已经翻译过的字符串.

如何仅在我的解决方案中CultureInfo.InvariantCulture设置了异常消息en-US

Mik*_*kis 5

异常应该包含他们自己的人类可读消息的概念是非常普遍的,但也是非常错误的.

在概念上,

异常传达的消息是异常的类型以及异常中包含的任何成员变量.

这也意味着:

对于您抛出的每个概念上不同的异常,您必须具有不同的异常类.

您不能拥有通配符"MyAllPurposeException",并通过在抛出它们的位置构造不同的人类可读消息来区分概念上不同的异常情况.试图从异常中理解的实体不应局限于人类:从概念上讲,catch块也有权能从代码抛出的异常中理解,至少在上下文中是这样.单元测试,但通常也在生产代码中.

为方便起见,我们通常只会将人类可读的消息填入我们的异常中,但这只是一个快速而肮脏的解决方案.当你有一个具有实际真实国际化需求的应用程序时,你不能做那样的黑客攻击.

所以,我的建议是永远不要向异常添加任何消息.让Message会员空白; 我甚至会说,string Message在原始Exception类中包含成员是代表语言运行时的设计者的错误.相反,异常的处理程序(知道如何处理它)应该决定是否应该为异常生成人类可读的消息,如果是,那么它是否应该只能由程序员或用户读取,从而,语言环境(文化)应该是什么.

从概念上讲,您将拥有一个二维表,在X轴上您将拥有区域设置,在Y轴上您将拥有异常类型名称,并且表中的每个单元格将包含一个字符串,例如"参数%d不能为null ",用正确的语言.第一列可能是中性语言环境,即包含程序员可读消息的不变文化.

当然,实际上它需要稍微复杂一点,因为你必须以某种方式读取异常的特定于类型的成员并将它们传递给字符串格式化函数.对于您可以控制的异常,您可以添加一个可覆盖的并按如下方式实现:

public ParameterCannotBeNullException extends MyException
{
    private final int parameterNumber;

    public ParameterCannotBeNullException( int parameterNumber )
    {
        this.parameterNumber = parameterNumber;
    }

    public override String FormatMessage( String localeSpecificMessage )
    {
        return String.Format( localeSpecificMessage, parameterNumber );
    }
}
Run Code Online (Sandbox Code Playgroud)

对于您无法控制的异常,您可以拥有一个巨大的级联if语句,如下所示:

String formatString = (look it up by exception.GetType() and locale)
String message;
if( exception is SomeException )
{
     SomeException temp = (SomeException)exception;
     message = String.Format( formatString, temp.X );
}
else if( exception is SomeOtherException )
{
     SomeOtherException temp = (SomeOtherException)exception;
     message = String.Format( formatString, temp.A, temp.B );
}
else
{
     message = exception.GetType().Name;
}
Run Code Online (Sandbox Code Playgroud)

最后一件事:有时在编写代码时我们意识到我们遇到了可能发生错误的情况,因此需要抛出一个新的异常,但我们不想中断我们正在做的事情并去声明那个时候一个新的异常类.对于这种情况,我发现有一个GeneralPurposeException实际上在其构造函数中接受包含程序员可读消息的字符串是有用的.但是这个类包含了一个巨大的巨大评论,如下所示:

XXX FIXME TODO:

仅用于开发目的!

因此,班级一定不能投入生产!