重复位代码的设计模式/ C#技巧

我有一个WCF服务,它记录任何exception,然后将它们作为FaultExceptions抛出。

我正在做很多重复,例如在每种服务方法中。

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#技巧可以用来使这更优雅?

你在谈论AOP – Aspect Oriented Programming

这是我如何通过将“工作”作为lambda传递来实现的:

 public partial static class Aspect { public static T HandleFaultException( Func 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( Func fn ) { using ( var client = ... create client ... ) { return fn( client ); } } } 

所以:

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

然后,您可以包装您通常想要应用的所有方面并创建一个主方面。

我们的一个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中的逻辑。

对于特定于WCF,您可能会考虑添加自己的ErrorHandler。 这允许您在每次任何方法抛出exception时“注入”您自己的代码。

您可以这样设置:

 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 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) { } } 

这也允许您抛出各种exception并将它们转换为故障exception,稍后由WCF正确处理,而无需修改业务层或应用try / catch代码并在那里进行转换。

模板方法模式正在这样做,您可以使用C#的委托(或lambdas)轻松实现它而不需要inheritance。

您可能想尝试AspectF(不是我的): http : //www.codeproject.com/Articles/42474/AspectF-Fluent-Way-to-Add-Aspects-for-Cleaner-Main 。 这是由Omar Al-Zabir …… Dropthings框架的创建者,以及其他一些东西。 希望这对你有所帮助。

如果你只是抛出,所以在调用堆栈顶部传播exception,你可以避免在每个深层次上处理它,只需在最顶层(可能)级别捕获它并记录。 请记住, Exception对象也包含堆栈跟踪。

顺便说一下,如果你需要捕捉,在每一个级别上都记得使用throw ,那么调用堆栈不会受到影响,而是以你使用它的方式,它会。

您还可以尝试订阅global.asax文件中的Application_Error事件。

对于桌面应用程序, AppDomain存在UnhandledException事件。

一般来说,您可以编写一些exception处理程序来为您执行冗余作业:

 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"); } } 

如果您的问题是关于如何更快地使您当前的模式与您合作,您可以通过创建一个Snippet重复该样板代码

   
trylog trylog