2012-12-15 46 views
9

我知道等待不能用在catch子句中。 但是我還沒有真正面臨與此有關的問題,直到現在...。尋找工作環繞

基本上我有一個層負責接收傳入的請求,處理它們,從它們構建消息並將消息傳遞到另一層負責發送消息。

如果在發送消息過程中出現問題,則會引發自定義異常並由消息發送層捕獲。此時,應該在DB中插入此消息的失敗記錄(需要一些時間,如async),並且應該將異常傳播到負責向發出請求的客戶端發送錯誤響應的上層。

這裏是下面說明的目的一些非常簡化的代碼:

public async Task ProcessRequestAsync(Request req) 
{ 
    int statusCode = 0; 

    try 
    {  
     await SendMessageAsync(new Message(req));   
    } 
    catch(MyCustomException ex) 
    { 
     statusCode = ex.ErrorCode; 
    } 

    await SendReponseToClientAsync(req, statusCode); 
} 


public async Task SendMessageAsync(Message msg) 
{ 
    try 
    {   
     // Some async operations done here 
    } 
    catch (MyCustomException ex) 
    {  
     await InsertFailureForMessageInDbAsync(msg, ex.ErrorCode); // CAN'T DO THAT 
     throw; 
    } 
} 

當然請求處理層一無所知DB,它只是在電荷建立消息時,將消息傳遞給消息處理層,並向客戶端發送響應(正面或負面)。

所以我認爲它是有道理的......如果發生異常,我的「業務」層希望在數據庫中插入一個失敗記錄並重新拋出異常,以便「請求」處理層可以做必要的事情上下文,向客戶端發送否定響應)。

是否應該以這種方式使用異常?對我來說這似乎很乾淨,但是我不能在catch子句中等待這一事實,這讓我認爲設計中可能存在代碼異味(即使我在一層中處理異常然後重新推出它以用於上層做一些不同的處理以及對我來說聽起來像是正是例外情況)。

圍繞這個有什麼想法嗎?

謝謝!

+1

好像你可以在發送層捕獲異常,並簡單地返回一個狀態對象給調用者。你甚至可以使這個狀態對象的一個​​字段成爲'Exception'類型,這樣如果需要的話,它可以被重新拋出。 – dlev

+1

你爲什麼不直接調用這個函數而不等它呢? – Rafael

+0

@dlev:感謝這個想法,對於我來說,在某些地方混合返回狀態並在其他地方使用例外情況仍然感覺不太好。必須在返回狀態代碼(或對象)或拋出異常之間作出選擇,但不要混合兩者(這是我記得從「良好」報告錯誤設計準則中得到的結果)。 – darkey

回答

6

我也遇到過幾次。

拉斐爾評論,你可以忽略的InsertFailureForMessageInDbAsync結果:

public async Task SendMessageAsync(Message msg) 
{ 
    try 
    {   
    // Some async operations done here 
    } 
    catch (MyCustomException ex) 
    {  
    var _ = InsertFailureForMessageInDbAsync(msg, ex.ErrorCode); 
    throw; 
    } 
} 

注意,從InsertFailureForMessageInDbAsync任何異常將被默認忽略。

你的其他選擇是更復雜的:

public async Task DoSendMessageAsync(Message msg) 
{ 
    // Some async operations done here 
} 

public async Task SendMessageAsync(Message msg) 
{ 
    var task = DoSendMessageAsync(msg); 
    MyCustomException exception = null; 
    try 
    { 
    await task; 
    return; 
    } 
    catch (MyCustomException ex) 
    { 
    exception = ex; 
    } 

    await Task.WhenAll(task, InsertFailureForMessageInDbAsync(msg, exception.ErrorCode)); 
} 

這將異步處理異常,並返回一個Task具有真正AggregateExption(含兩個例外,如果InsertFailureForMessageInDbAsync並拋出一個)。

不幸的是,await將忽略第二個異常。如果你真的想錯過了所有的異常,你可以像這樣的東西代替的最後一行(await Task.WhenAll...):

Exception exception2 = null; 
try 
{ 
    await InsertFailureForMessageInDbAsync(msg, exception.ErrorCode); 
} 
catch (Exception ex) 
{ 
    exception2 = ex; 
} 

if (exception2 == null) 
    throw new AggregateException(exception); 
else 
    throw new AggregateException(exception, exception2); 

但是,這是非常複雜的,而不是正是你想要重複一種模式。如果可能的話,我會忽略Rafael推薦的日誌記錄結果。

+0

尼斯斯蒂芬!會說我希望有一個更簡單的方法來圍繞這個,所以我沒有像你在反射中那麼遠。但畢竟,沒有簡單的方法來處理這個問題;) 無論如何非常感謝您的回答,我想我會走更簡單的方式,但至少我會知道如果真的走「硬」的路需要。 – darkey