2008-12-05 17 views
4

我有很多WebService方法,都包含一些模板代碼,將實際工作封裝在try/catch/finally中,並在catch/finally中執行相同的任務。所以作爲封裝所有共享catch/finally的東西的一種方式,我寫了一個簡單的泛型。使用泛型來封裝通用方法的工作

這個工作,真的消除了一堆重複的代碼,但感覺klunky,語法非常鈍。每當我回到這個時候,我的大腦都會扭曲,試圖弄清楚它(一個明顯的跡象表明它不是一個好的設計)。我在尋找關於這是否是一件瘋狂事情的​​反饋,以及是否有更好的方法來處理它。

這裏是我的模板:

public delegate T2 RestfulServiceRequest<T1, T2>(T1 req); 

static class RestfulService 
{ 
    public static T2 ExceptionHandler<T1, T2>(RestfulServiceRequest<T1, T2> serviceCall, T1 req) 
    { 
     if (req == null) 
      throw new BadRequestException(new ArgumentNullException("Invalid or missing request object")); 

     try 
     { 
      return serviceCall(req); 
     } 
     catch (RestfulException e) 
     { 
      // log it and rethrow 
      Logger.Write(e); 
      throw; 
     } 
     catch (Exception e) 
     { 
      Logger.Error(e); 

      // wrap in a consistent exception for propagation back to caller 
      throw new InternalServerException(e); 
     } 
     finally 
     { 
      Logger.Debug("Complete"); 
     } 
    } 
} 

}

這裏是它的用法:

public class Initialization : IInitialization 
{ 
    // MyMethod thas uses the template 
    public ApplianceInitResp CreateApplianceServer(ApplianceInitReq req) 
    { 
     return RestfulService.ExceptionHandler<ApplianceInitReq, ApplianceInitResp>(delegate(ApplianceInitReq x) 
     { 
      // do some work 
      return new ApplianceInitResp(); 
     }, req); 
    } 
} 

}

回答

2

我會改變

public static T2 ExceptionHandler<T1, T2>(RestfulServiceRequest<T1, T2> serviceCall, T1 req) 

public static T2 Invoke<T1, T2>(this T1 req, RestfulServiceRequest<T1, T2> serviceCall) 

這將通話切換到

public class Initialization :IInitialization { 
    public ApplianceInitResp CreateApplianceServer(ApplianceInitReq req) { 
     return req.Invoke(r => { 
      // do some work 
      return new ApplianceInitResp(); 
     }); 
    } 
} 
+0

我不得不承認,我不明白'這個T1請求'是如何工作的。編譯器如何找出使用正確的泛型? – BrettRobi 2008-12-18 19:20:53

0

我會同意,這感覺有點笨重但是,我並不是馬上看到「完美」的方式來重新工作,以儘量減少重複。

我在想的唯一的事情是,如果你能以某種方式使用接口或將其分解一點,我不確定我該怎麼做,但我可以說,對於正在進行的更改,我不會我想我會離開這個,至少不是沒有非常好的文檔。

1

有一件事會讓它變得更簡潔,就是定義你的請求/響應對象實現的接口。然後,您可以擺脫泛型,轉而使用接口。請注意,我認爲名稱更改更能描述您真正想要做的事情。

public interface IServiceResponse { ... } 
public class ApplianceInitResp : IServiceResponse { ... } 
public interface IServiceRequest { ... } 
public class ApplianceInitReq : IServiceRequest { ... } 

public delegate IServiceResponse RestfulServiceRequest(IServiceRequest req); 

static class RestfulService 
{ 
    public static IServiceResponse 
     Invoke(RestfulServiceRequest serviceCall, IServiceRequest req)   
    { 
     if (req == null) 
      throw new BadRequestException(...inner-exception...); 
     try 
     { 
      return serviceCall(req); 
     } 
     catch (RestfulException e) 
     { 
      Logger.Write(e); 
      throw;    
     } 
     catch (Exception e) 
     { 
      Logger.Error(e); 
      throw new InternalServerException(e); 
     } 
     finally 
     { 
      Logger.Debug("Complete"); 
     } 
    } 
} 

public class Initialization : IInitialization 
{ 
    // MyMethod thas uses the template 
    public ApplianceInitResp CreateApplianceServer(ApplianceInitReq req) 
    { 
      return RestfulService.Invoke(
        delegate(ApplianceInitReq x) 
        { 
         // do some work 
         return new ApplianceInitResp(); 
        }, 
        req); 
    } 
} 
+0

我喜歡這個解決方案,因爲它是更容易理解。我看到的唯一缺點是要求所有服務操作都使用從接口派生的參數。除非我需要返回本機類型,否則沒什麼大不了的。 – BrettRobi 2008-12-18 00:20:06

1

我建議你尋找提供AOP功能的框架(如Spring.NET,Unity)。這些將幫助您降低CreateApplianceServer()的照顧入口/出口和異常記錄的使用方面來電單純

public ApplianceInitResp CreateApplianceServer(ApplianceInitReq req) 
{ 
    // Do some work 
    return new ApplianceInitResp(); 
} 

。也許,如果你有一些共同的參數,你也可以將參數驗證插入到方面。

當然,會有一個學習曲線稅,但相信我,值得它的結果。您將顯着減少方法中樣板代碼的數量。

0

我認爲你在做什麼很好。這種編碼在腳本語言中很常見 - 但由於它們是動態輸入的,因此它不那麼冗長。它在功能語言中也很常見 - 但之後就不那麼冗長了,因爲它們有這麼好的類型推斷!在C#中,您受到當前類型推斷系統限制的影響,但在版本4中會更好。

另一種方法是使用擴展的接口或類來封裝您的工作。當你這樣做的時候,在調用的時候你有更少的類型名稱,但是整體上有更多的代碼。

相關問題