2013-05-20 86 views
4

在我的一個類中,我調用了一個對其有一些錯誤處理的倉庫。我想重構錯誤處理代碼,因爲它是相當重複的,唯一真正改變的是消息。重構異常處理

我的代碼目前看起來是這樣的:

public IList<User> GetUser() 
{ 
    try 
    { 
     return _repository.GetUsers(); 
    } 
    catch (WebException ex) 
    { 
     ErrorMessages.Add("..."); 
     _logger.ErrorException("...", ex); 
    } 
    catch (SoapException ex) 
    { 
     ErrorMessages.Add("..."); 
     _logger.ErrorException("...", ex); 
    } 
    ... etc 
} 

我可以給它接受一個錯誤信息值和記錄信息值的另一種方法的調用替換我的catch塊的行。不過,我想我也可以使用一個動作參數來做到這一點,但我在使用Func <>和動作<>時很缺乏經驗,並且實際上並沒有看到使用其中一種方法會有什麼好處。

我的問題是真正重構這段代碼的最佳方式是什麼,以及爲什麼一種方式會比另一種方式受益(就像我上面的示例一樣)。

感謝您的任何幫助。

+0

爲什麼? http://blog.gauffin.org/2010/11/do-not-catch-that-exception/ – jgauffin

+0

「在距離用戶最近的層中嘗試/捕獲所有內容都可以。」。最終,這個結果將返回到一個視圖 – Serberuss

回答

3

假設異常類型都是一樣的,但消息是不同的,你可以這樣做:

static public T Try<T>(string webMessage, string soapMessage, Func<T> func) 
{ 
    try 
    { 
     return func(); 
    } 
    catch (WebException ex) 
    { 
     ErrorMessages.Add(webMessage); 
     _logger.ErrorException(webMessage, ex); 
    } 
    catch (SoapException ex) 
    { 
     ErrorMessages.Add(soapMessage); 
     _logger.ErrorException(soapMessage, ex); 
    } 
} 

這種嘗試,方法將使用Func<T>類型的委託來調用一個函數,並返回其值。該函數將在同一個try-catch塊內。消息通過參數提供。現在,在你的代碼別的地方,你可以調用這個喜歡:

var users = Try("My web message.", "My soap message.",() => _repository.GetUsers()); 

或者,你的情況甚至更短(當不使用參數):

var users = Try("My web message.", "My soap message.", _repository.GetUsers); 

當然你也可以修改和安排參數Try根據您的喜好。

如果您使用和不使用返回類型的混合方法,最好不要使用Func,而是使用Action。這將能夠滿足所有的情況:

static public void Try(string webMessage, string soapMessage, Action action) 
{ 
    try 
    { 
     action(); 
    } 
    catch (WebException ex) 
    { 
     ErrorMessages.Add(webMessage); 
     _logger.ErrorException(webMessage, ex); 
    } 
    catch (SoapException ex) 
    { 
     ErrorMessages.Add(soapMessage); 
     _logger.ErrorException(soapMessage, ex); 
    } 
} 

但是這種解決方案使代碼一點點比較難讀/維持:

IList<User> users; 
Try("My web message.", "My soap message.",() => users = _repository.GetUsers()); 
+0

請注意,返回類型'T'會使處理void方法有點尷尬。 –

+0

絕對如此。我會更新我的答案。 –

+0

另一個你不應該僅僅因爲你能做到的事情就是這樣的例子。雖然這個問題的答案很好。 – gbjbaanb

3

您可以使用lambda表達式,以幫助這一點。

如果將通用錯誤處理程序定義爲接受Action類型的參數,則可以在錯誤處理程序中調用該操作。

您不需要擔心返回值,因爲您在調用點寫入的lambda可以處理該問題。

例如,你的一般處理程序看起來是這樣的:

public void AttemptAction(Action action) 
{ 
    try 
    { 
     action(); 
    } 
    catch (WebException ex) 
    { 
     ErrorMessages.Add("..."); 
     _logger.ErrorException("...", ex); 
     // Rethrow? 
    } 
    catch (SoapException ex) 
    { 
     ErrorMessages.Add("..."); 
     _logger.ErrorException("...", ex); 
     // Rethrow? 
    } 
} 

然後你可以使用這樣的:

public IList<User> GetUser() 
{ 
    IList<User> result = null; 

    AttemptAction(() => result = _repository.GetUsers()); 

    return result; 
} 
+0

謝謝你的回答。但是,我認爲它需要反過來,因爲所有調用的錯誤消息都不相同。有一個GetUsers調用,但也有其他的,如GetCategories。我應該在我原來的帖子中提到過,對不起 – Serberuss

+0

@Serberuss沒關係 - 這是一種普遍的方法。如果需要,可以像MartinMulder的回答一樣傳遞錯誤消息。 –

0

您可以使用面向方面編程http://en.wikipedia.org/wiki/Aspect-oriented_programming。 將所有重複代碼放置到稱爲aspect的特殊類的想法。

您的代碼看起來像在PostSharp

[ExceptionLogger] 
public IList<User> GetUser() 
{ 
    return _repository.GetUsers(); 
} 
public class ExceptionLogger: OnMethodBoundaryAspect 
{ 
    //getting _logger and ErrorMessages 
    public override void OnException(MethodExecutionArgs args) 
    { 
     ErrorMessages.Add("..."); 
     _logger.ErrorException("...", ex); 
    } 
} 

對於C#,你CCAN使用PostSharp,Castle.Windsor或統一框架。