2016-11-29 173 views
3

我們有庫的代碼看起來是這樣的:異常記錄

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

回答

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。日誌記錄不應該混淆代碼。

+0

原始異常包含在代碼中,忘記將其添加到僞代碼中。我只是用'扔',但是我們的觀點是這個例外不夠友好。 – Silencer

+0

在我看來,異常消息不應該呈現給用戶,而應該取決於異常的類型。當錯誤只是說「MethodXXX中的錯誤」時,您希望用戶採取什麼樣的行動。它不知道這個方法。也不在乎。用戶想知道他是否可能重試或錯誤的副作用是什麼。 –

+0

我也強烈建議不要拋出基本的'Exception'類。你應該總是使用一個更具體的異常類型,否則就不可能在你想處理異常的地方編寫特定的catch子句。我想不出一個很好的理由來拋出(或者甚至創建)'Exception'。 – Kyle

0

有幾種方法可以解決這個問題。最簡單的就是使用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

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作爲一個深入的例子。

+0

謝謝,這是一個很好的答案,但彼得Bons是第一個,壞我不能標記兩個答案:( – Silencer