异常记录

问题描述:

我们有库的代码看起来是这样的:异常记录

public class PieRepository 
{ 

    public void AddCherryPie(string incredientA) 
    { 
     try{ 
      ... 
     } 
     catch(Exception ex){ 
      log("Error in AddCherryPie(" + incredientA + ")"); 
      throw new Exception("Error in AddCherryPie(" + incredientA + ")", ex); 
     } 
    } 

    public void AddApplePie(string incredientA, string incredientB) 
    { 
     try{ 
      ... 
     } 
     catch(Exception ex){ 
      log("Error in AddApplePie(" + incredientA + "," + incerdientB + ")"); 
      throw new Exception("Error in AddApplePie(" + incredientA + "," + incredientB ")", ex); 
     } 
    } 
} 

所以这try -> catch -> log -> throw new是存在于大多数的存储库的方法和项目等重要方法。

我们今天有一个争论,因为我从来没有见过有人提出这样的错误处理类型,但主要论点是我们和支持人员需要确切地知道发生了什么,以及任何其他类型的异常处理不会发生, t给我们这个... 有人可以告诉我这是好吗?

编辑:抛出错误时添加原始异常消息。

+0

代码捕获异常,然后不使用它,而是抛出一个新的异常,它将不包含任何有用的调试信息。 “我们需要确切地知道发生了什么”的论点是没有道理的,因为没有StackTrace等,你无法确切地知道发生了什么。您至少应该在某处记录异常,然后向用户提供有用的消息。 – Equalsk

+0

对于日志方面,您可以尝试NLog或其他第三方解决方案(我对NLog非常满意) – Tuco

永远不要创建一个新的异常,使用throw或者至少包含原始异常作为内部异常重新抛出异常。否则,堆栈跟踪进一步上移链不再正确。它将起源于你做过的地方throw new Exception

更好的是:

public class PieRepository 
{ 
    public void AddCherryPie(string incredientA) 
    { 
     try{ 
      ... 
     } 
     catch(Exception ex){ 
      log("Error in AddCherryPie(" + incredientA + ")"); 
      throw 
     } 
    } 

public void AddApplePie(string incredientA, string incredientB) 
    { 
     try{ 
      ... 
     } 
     catch(Exception ex){ 
      log("Error in AddApplePie(" + incredientA + "," + incerdientB + ")"); 
      throw new Exception("Error in AddApplePie(" + incredientA + "," + incredientB ")", ex); // add original exception as inner exception! 
     } 
    } 
} 

就个人而言,我会真的建议只使用throw代替throw new Exception("...", originalException)。通过总是抛弃原始异常,您无法决定后面在流程中要做什么。向用户展示什么错误?对于数据库错误或验证错误(如给出消息“数据库不可用”或“找不到成分A”),操作可能与编程错误不同。

整体方法是有效的。由于您知道错误的方法和上下文的参数,因此尽早记录它是个好习惯。通过重新推送,您可以再次处理UI中的错误,方法是向用户呈现消息或根据异常的类型采取其他操作。

有关错误消息的一些想法阅读:http://blog.ploeh.dk/2014/12/23/exception-messages-are-for-programmers/

现在,如果你真的想为每一个方法使用面向方面编程做到这一点(AOP,见https://en.wikipedia.org/wiki/Aspect-oriented_programming)。使用这种技术,您可以使用例如属性来完成。使用PostSharp例如:http://doc.postsharp.net/exception-handling。日志记录不应该混淆代码。

+0

原始异常包含在代码中,忘记将其添加到伪代码中。我只是用'扔',但是我们的观点是这个例外不够友好。 – Silencer

+0

在我看来,异常消息不应该呈现给用户,而应该取决于异常的类型。当错误只是说“MethodXXX中的错误”时,您希望用户采取什么样的行动。它不知道这个方法。也不在乎。用户想知道他是否可能重试或错误的副作用是什么。 –

+0

我也强烈建议不要抛出基本的'Exception'类。你应该总是使用一个更具体的异常类型,否则就不可能在你想处理异常的地方编写特定的catch子句。我想不出一个很好的理由来抛出(或者甚至创建)'Exception'。 – Kyle

有几种方法可以解决这个问题。最简单的就是使用Exception类的数据属性:

public void AddApplePie(string incredientA, string incredientB) 
{ 
    try 
    { 
     ... 
    } 
    catch(Exception ex) 
    { 
     ex.Data["IncredientA"] = incredientA; 
     ex.Data.Add("IncredientB", incredientB); 
     throw; 
    } 
} 

或者,您可以创建一个包含附加信息的自定义异常,然后抛出一个与原始异常作为内部异常。

为了给你一个想法,考虑以下因素:

public class PieRepositoryException : Exception 
{ 
    public PieRepositoryException(string message, Exception innerException, params string[] ingredients):base(message, innerException) 
    { 
     Ingredients = ingredients; 
    } 

    public property string[] Ingredients { get; private set; } 
} 

然后你可以这样做:

public void AddApplePie(string incredientA, string incredientB) 
{ 
    try 
    { 
     ... 
    } 
    catch(Exception ex) 
    { 
     throw new PieRepositoryException("Error adding apple pie.", ex, incredientA, incredientB); 
    } 
} 

然后你更高级别的代码可以实施一项战略,要么赶上特殊的例外,抓通用的,并将其所有属性输出到日志或使用格式化程序输出到基于类型的日志。

+0

在一个存储库中,参数可以是从int到object的所有东西,这并不是一个很大的问题,因为它们都可以表示为字符串,但是问题的主要思想是如何实现'try - > catch - > log - >在所有方法中抛出new。 – Silencer

由于您似乎在每个方法中都使用完全相同的行为,我强烈建议使用面向方面的编程框架。

通过这种方式,您可以定义一个方面(属性),其中包含您的自定义异常处理逻辑,并避免在每种方法中都具有相同的样板代码。

你的类可能是这样的,如果你使用方面:

public class PieRepository 
{ 
    [LogExceptions] 
    public void AddCherryPie(string incredientA) 
    { 
     ... 
    } 
} 

而且您方面看起来是这样的:

public class LogExceptionsAttribute : OnExceptionAspect 
{ 
    public override void OnException(MethodExecutionArgs args) 
    { 
     // Create a log message, you can access the method info and parameters 
    } 
} 

如果属性的方法来使用方面,各方面会每次执行方法时都要调用,因此请确保使用支持编译时编织的框架(而不是运行时编织,这会对应用程序的性能产生巨大影响)。

查看http://doc.postsharp.net/exception-handling作为一个深入的例子。

+0

谢谢,这是一个很好的答案,但彼得Bons是第一个,坏我不能标记两个答案:( – Silencer