2013-03-04 64 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>