2013-03-04 69 views
10

我有一個WCF服務,它記錄任何異常,然後將它們作爲FaultExceptions引發。重複位代碼設計模式/ C#技巧

我做了很多重複例如在每個服務方法中。

try { 
    // do some work 

} 
catch(Exception ex) 
{ 
    Logger.log(ex); 

    // actually will be Fault Exception but you get the idea. 
    throw ex; 
} 

我正在尋找更優雅的方式來做到這一點,因爲我正在剪切和粘貼try/catch遍及每個服務。

是否有設計模式/ C#技巧可以用來使這更優雅?

+16

請注意「throw ex;」失去原來的調用堆棧,而「拋出」纔不是。 – Polyfun 2013-03-04 14:04:46

+1

嘗試的身體是否也一樣? – christopher 2013-03-04 14:06:11

+1

你幾乎看着Aspect Orientated Programming這裏 - http://msdn.microsoft.com/en-us/library/aa288717%28v=vs.71%29.aspx。很多像PostSharp這樣的第三方解決方案都有異常處理方面的問題。另一個(更糟糕的)解決方案是捕獲應用程序級別的所有異常(例如AppDomain_UnhandledException),而不是使用本地try catch塊。我說更糟,因爲它有效地將你綁定到沒有本地的異常處理,或者用try/catch拋出你的代碼(拋出) – dash 2013-03-04 14:07:08

回答

1

Template method pattern正在這樣做,你可以很容易地實現它,而不需要使用C#的委託(或lambda表達式)的繼承。

0

如果你簡單地拋出,所以在調用堆棧頂部傳播異常,你可以避免在每個深層次上處理它,只要在最上面(可能)級別上記錄它並記錄下來。請記住,Exception對象也包含堆棧跟蹤。

如果順便說一下,你需要在每個級別上捕捉,請記住使用throw,所以調用堆棧不會受到影響,而是以您使用它的方式進行調用。

2

對於WCF的具體情況,您可以考慮添加自己的ErrorHandler。這允許您在每次任何方法拋出異常時「注入」自己的代碼以執行。

你可以將它設置這樣的:

serviceHost.Description.Behaviors.Add(new ErrorHandlerBehavior()); //Add your own ErrorHandlerBehaviour 

public class ErrorHandlerBehavior : IErrorHandler, IServiceBehavior 
{ 
    private static readonly Logger log = LogManager.GetCurrentClassLogger(); 

    public bool HandleError(Exception error) 
    { 
     if (error is CommunicationException) 
     { 
      log.Info("Wcf has encountered communication exception."); 
     } 
     else 
     { 
      // Log 
     } 

     return true; 
    } 

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault) 
    { 
     //Here you can convert any exception to FaultException like this: 
     if (error is FaultException) 
      return; 

     var faultExc = new FaultException(error.Message); 
     var faultMessage = faultExc.CreateMessageFault(); 

     fault = Message.CreateMessage(version, faultMessage, faultExc.Action); 
    } 

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, 
     BindingParameterCollection bindingParameters) 
    { 
    } 

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
    { 
     foreach (ChannelDispatcherBase channelDispatcher in serviceHostBase.ChannelDispatchers) 
     { 
      var channelDisp = channelDispatcher as ChannelDispatcher; 

      if (channelDisp != null) 
       channelDisp.ErrorHandlers.Add(this); 
     } 
    } 

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
    { 
    } 
} 

這也可以讓你拋出各種異常和將它們轉換爲故障異常,這是正確的WCF處理以後,無需修改業務層或者應用try/catch代碼並在那裏轉換它們。

5

我們在我們的WCF服務,我已經通過使用一個輔助委託解決一個有一個類似的問題:

public static void ErrorHandlingWrapper(Action DoWork) 
{ 
    try { 
     DoWork(); 
    } 
    catch(Exception ex) 
    { 
     Logger.log(ex); 

     // actually will be Fault Exception but you get the idea. 
     throw; 
    } 
} 

用法:

public void MyMethod1() 
{ 
    ErrorHandlingWrapper(() => { 
     // do work 
    }); 
} 

public void MyMethod2() 
{ 
    ErrorHandlingWrapper(() => { 
     // do work 
    }); 
} 

你仍然必須重複包裝,但代碼少得多,您可以在一個地方修改try..catch中的邏輯。

9

你說的是AOP - Aspect Oriented Programming

下面是如何通過傳遞 「工作」 作爲一個lambda做到這一點:

public partial static class Aspect 
{ 
    public static T HandleFaultException<T>(Func<T> fn) 
    { 
    try 
    { 
     return fn(); 
    } 
    catch(FaultException ex) 
    { 
     Logger.log(ex); 
     throw; 
    } 
    } 
} 

然後使用它:

return Aspect.HandleFaultException(() => 
    { 
    // call WCF 
    } 
); 

還有其他方法可以實現相同的目標,甚至是一些商業產品,但我認爲這種方式是最明確和最靈活的。

例如,您可以編寫創建並配置爲您的客戶一個方面:

public partial static class Aspect 
{ 
    public static T CallClient<T>(Func<Client, T> fn) 
    { 
    using (var client = ... create client ...) 
    { 
     return fn(client); 
    } 
    } 
} 

等:

return Aspect.CallClient(client => 
    { 
    return client.Method(...); 
    } 
); 

然後,你可以用你通常需要的方面申請並創建一個主要方面。

0

一般來說,你可以寫,做多餘的工作給你一些異常處理程序:

public abstract class ExceptionHandler 
{ 
    /// Returns true if the exception is handled; otherwise returns false. 
    public abstract bool Handle(Exception ex); 

    protected void Log(Exception ex) 
    { 
     // Log exception here 
    } 
} 

public class FileExceptionHandler : ExceptionHandler 
{ 
    public override bool Handle(Exception ex) 
    { 
     this.Log(ex); 

     // Tries to handle exceptions gracefully 
     if (ex is UnauthorizedAccessException) 
     { 
      // Add some logic here (for example encapsulate the exception) 
      // ... 
      return true; 
     } 
     else if (ex is IOException) 
     { 
      // Another logic here 
      // ... 
      return true; 
     } 

     // Did not handled the exception... 
     return false; 
    } 
} 

public class Program 
{ 
    private static void Main(string[] args) 
    { 
     try 
     { 
      // File manipulation 
      throw new IOException(); 
     } 
     catch (Exception ex) 
     { 
      if (!new FileExceptionHandler().Handle(ex)) 
      { 
       // Exception not handled, so throw exception 
       throw; 
      } 
     } 

     Console.WriteLine("end"); 
    } 
} 
0

如果你的問題是如何使你的當前模式更快地爲你工作,你可以重複鍋爐通過創建一個Snippet

<CodeSnippets 
    xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> 
    <CodeSnippet Format="1.0.0"> 
     <Header> 
      <Title> 
       trylog 
      </Title> 
      <Shortcut> 
       trylog 
      </Shortcut> 
     </Header> 
     <Snippet> 
      <Code Language="CSharp"> 
       <![CDATA[try { 
    // do some work 

} 
catch(Exception ex) 
{ 
    Logger.log(ex); 

    // actually will be Fault Exception but you get the idea. 
    throw ex; 
}]]> 
      </Code> 
     </Snippet> 
    </CodeSnippet> 
</CodeSnippets>