所有方法中的异常记录

时间:2016-11-29 21:19:27

标签: c# .net exception-handling

我们的存储库代码如下所示:

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存在于大多数存储库方法和项目中的其他重要方法中。

我们今天对此有一个争论,因为我从未见过有人建议这种类型的错误处理,但主要的论点是我们和支持需要确切知道发生了什么,并且任何其他类型的异常处理都不会#39 ;给我们这个...... 有人能说出这是否合适?

修改:投掷错误时添加了原始异常消息。

3 个答案:

答案 0 :(得分:3)

永远不要创建新的异常。只需使用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。记录不应该使代码混乱。

答案 1 :(得分:1)

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

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

如果您使用方面,您的课程可能如下所示:

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

答案 2 :(得分:0)

你可以通过几种方式解决这个问题。最简单的方法是使用Exception类的Data属性:

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); 
    }
}

然后,您的更高级代码可以实现一种策略,以捕获特定异常,捕获通用异常并将所有属性输出到日志,或使用格式化程序根据类型输出到日志。