2014-07-18 13 views
4

應用程序體系結構爲:MVVM [Views - > ViewModels] - > Repositories - > API。跨所有視圖模型處理令牌過期事件的策略

API層可能會拋出一個TokenExpiredException異常,我們最終想要在UI層處理(通過顯示消息框並重定向到登錄)。

今天,我們的虛擬機,象這樣的倉庫交互:

SomeCommand { 
    await _repo.DoSomethingAsync(); 
} 

我的問題是找到一個很好的模式來處理從API層這一例外。我能想到的3種方法:

1)裹在BaseViewModel方法負責捕捉和處理這個視圖模型無關異常的每個庫調用。

SomeCommand { 
    await base.RepoRequest(() => _repo.DoSomethingAsync()); 
} 

其中BaseViewModel將有:

RepoRequest(action) { 
    try { action() } 
    catch (TokenExpiredException) { 
     // show message box 
     // redirect 
    } 

任何其他異常,例如驗證錯誤,將在VM處理。我在這裏看到的問題是,忘記使用這種模式太容易了。我可能會直接在某處調用存儲庫,並錯過處理異常。

2)每個VM捕獲該異常

SomeCommand { 
    try { await _repo.DoSomethingAsync(); } 
    catch (InvalidUsernameException) { ... } 
    catch (TokenExpiredException) { 
     // show message box 
     // redirect 
    } 

不是真的不同於1),同樣的問題,需要更多的代碼重複。

3)使用事件聚合器將消息從API層發佈到BaseViewModel

ApiRequest { 
    var response = await _httpClient.ExecuteAsync<..>(...); 

    if (response.ErrorId == "InvalidUsername") 
     throw new InvalidUsernameException(); 
    else if (response.ErrorId == "TokenExpired") 
     EventAggregator.Publish(new TokenExpiredException()); 
} 

BaseViewModel

onMessage(TokenExpiredException e) { 
    // show message box 
    // redirect 
} 

這具有留下的所有VM(但基)自由的佈線起來的優點。 缺點是我很猶豫要使用事件聚合器1)在所有2)API層。 我們正在使用mvvm-light,這意味着在我們更深層的圖層中僅針對Messenger(其事件聚合器)引用這些庫。

有沒有人有關於如何幹淨地實現此功能的建議?

回答

1

我不得不弄清同樣的問題,但是我在城堡wcf設施上使用了WCF,這對我有幫助,因爲該設施已經有攔截呼叫的point of extension。所以我剛創建了我的自定義AbstractWcfPolicy並攔截了我想要管理的所有異常。

有了你可以考慮使用一個代理類,基於castle dynamic proxy,那樣您的通話將保持await _repo.DoSomethingAsync();但木下你的ExceptionInterceptor會攔截所有的異常,做你想做同樣的想法:

[Serializable] 
public class Interceptor : IInterceptor 
{ 
    public void Intercept(IInvocation invocation) 
    { 
     Console.WriteLine("Before target call"); 
     try 
     { 
      invocation.Proceed(); 
     } 
     catch(Exception) 
     { 
      Console.WriteLine("Target threw an exception!"); 
      throw; 
     } 
     finally 
     { 
      Console.WriteLine("After target call"); 
     } 
    } 
} 

然後,您可以拋出一些您可能想拋出的異常,對於某些異常或所有異常,您還可以實現發佈 - 訂閱(全局消息代理,messenger in MVVM Light)機制,例外,然後在你的應用程序的任何一點你都可以訂閱那些呃漫遊並做一些事情(記錄,以不顯眼的方式向用戶顯示錯誤等)。

+0

有趣的與溫莎方法平行。訂閱在基本VM類中偵聽消息後,構造函數應該在事件被觸發時捕獲,而不必通過過多冗餘處理來拋出應用程序。 –

+1

@IanPatrickHughes實際上都是關於[面向方面的編程](http://en.wikipedia.org/wiki/Aspect-oriented_programming),避免改變任何代碼行,而是注入行爲,這是一種情況,其中AOP完全適合,另一種情況是爲了記錄目的,有時在wpf應用程序中,我同時執行這兩種操作,攔截記錄異常並管理某種通知返回到應用程序。另外在WCF中,真正重要的是攔截所有的異常,並且總是避免拋出一個不是FaultException的異常,因此我攔截並記錄內部異常。 –