在用wcf做为单纯的服务端的时候,发生错误是常有的事情,特别是在调用其他系统提供的接口的时候,发生的一些错误总是让人摸不着头脑,严重影响了错误的定位。做.net web开发的时候,我们可以在Global里面直接捕获全局异常,那么wcf是否也可以定义全局异常处理?对于已有的系统,逐个方法添加异常处理是很不现实而且还会伴随很大的风险,那么我们肯定希望这种改动尽可能的小甚至不用改动。下面分享一下实现的方法:
利用Attribure和IServiceBehavior实现wcf全局异常处理
这种方式使用方便,基本不用改动原有的代码,效果如下:
class="code_img_closed" src="/Upload/Images/2013100916/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('82740ecc-1db5-4379-b067-8a3f2e4d42b5',event)" src="/Upload/Images/2013100916/2B1B950FA3DF188F.gif" alt="" />1 [WcfGlobalExceptionOpreationBehaviorAttribute(typeof(WcfGlobalErrorHandler))] 2 public class DemoService : IDemoService 3 { 4 }View Code
或者
1 [WcfGlobalServiceInterceptor] 2 public class DemoService : IDemoService 3 { 4 }View Code
以上的两种方式在具体实现上稍有不同,具体实现方式如下:
在此之前先来看看IServiceBehavior接口提供的方法
1 public interface IServiceBehavior 2 { 3 void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters); 4 void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase); 5 void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase); 6 }
后面我们用到的是方法ApplyDispatchBehavior,这个方法会在服务Open的时候执行,我们可以在此注入我们自定义的异常处理程序,从而达到处理全局异常的目的(注:试验证明在服务Open之后再注入分发行为是无效的)。
形式一:IErrorHandler
可参考利用Attribute和IErrorHandler处理WCF全局异常1 private readonly Type _errorHandlerType; 2 public WcfGlobalExceptionOpreationBehaviorAttribute(Type handlerType) 3 { 4 _errorHandlerType = handlerType; 5 } 6 7 /// <summary> 8 /// 注入自定义异常处理程序 9 /// </summary> 10 /// <param name="serviceDescription"></param> 11 /// <param name="serviceHostBase"></param> 12 public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) 13 { 14 var handler = (IErrorHandler) Activator.CreateInstance(_errorHandlerType); 15 foreach (ChannelDispatcher dis in serviceHostBase.ChannelDispatchers) 16 { 17 dis.ErrorHandlers.Add(handler); 18 } 19 }View Code
原理就是我们为信道分发器注入自定义的ErrorHandler
1 /// <summary> 2 /// WCF全局异常处理 3 /// </summary> 4 public class WcfGlobalErrorHandler:IErrorHandler 5 { 6 /// <summary> 7 /// 启用错误相关处理并返回一个值 8 /// </summary> 9 /// <param name="ex"></param> 10 /// <returns>是否终止会话和上下文</returns> 11 public bool HandleError(System.Exception ex) 12 { 13 WcfGlobalEventHelper.FireException(new WcfGlobalException(ex.Message,ex.StackTrace,null,null)); 14 return true; 15 } 16 17 public void ProvideFault(System.Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault) 18 { 19 20 } 21 22 }View Code
形式二:IOperationBehavior
1 public class WcfGlobalOperationInterceptorAttribute:Attribute,IOperationBehavior 2 { 3 private string operationMethod; 4 5 public WcfGlobalOperationInterceptorAttribute(string methodName) 6 { 7 this.operationMethod = methodName; 8 } 9 10 public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 11 { 12 13 } 14 15 public void ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation) 16 { 17 18 } 19 20 public void ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation) 21 { 22 IOperationInvoker invoke = dispatchOperation.Invoker; 23 dispatchOperation.Invoker = CreateInvoker(invoke); 24 } 25 26 public void Validate(OperationDescription operationDescription) 27 { 28 29 } 30 31 protected OperationInvoker CreateInvoker(IOperationInvoker oldInvoker) 32 { 33 return new OperationInvoker(oldInvoker, this.operationMethod); 34 } 35 }View Code
实际上就是在ApplyDispatchBehavior方法中分发操作的Invoker进行了封装
1 /// <summary> 2 /// 使用消息提取的对象以及参数数组,并利用对该对象调用方法,然后返回该方法的返回值和输出参数 3 /// </summary> 4 public class OperationInvoker:IOperationInvoker 5 { 6 private string operationMethod;//方法名称 7 private IOperationInvoker invoker;//原invoker 8 private Stopwatch sw;//用于计时 9 10 public OperationInvoker(IOperationInvoker oldInvoker, string operationMethod) 11 { 12 this.invoker = oldInvoker; 13 this.operationMethod = operationMethod; 14 sw = new Stopwatch(); 15 } 16 17 public object[] AllocateInputs() 18 { 19 return this.invoker.AllocateInputs(); 20 } 21 /// <summary> 22 /// 从一个实例和输入对象的集合返回一个对象和输出对象的集合 23 /// </summary> 24 /// <param name="instance"></param> 25 /// <param name="inputs"></param> 26 /// <param name="outputs"></param> 27 /// <returns></returns> 28 public object Invoke(object instance, object[] inputs, out object[] outputs) 29 { 30 this.PreInvoke(instance, inputs); 31 object returnValue = null; 32 object invokeValue; 33 object[] objArray = new object[0]; 34 System.Exception ex = null; 35 try 36 { 37 invokeValue = this.invoker.Invoke(instance, inputs, out objArray); 38 returnValue = invokeValue; 39 outputs = objArray; 40 } 41 catch (System.Exception e) 42 { 43 ex = e; 44 returnValue = null; 45 outputs = null; 46 } 47 finally 48 { 49 this.PostInvoke(instance,returnValue,objArray,ex); 50 } 51 return returnValue; 52 } 53 54 public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) 55 { 56 this.PreInvoke(instance,inputs); 57 return this.invoker.InvokeBegin(instance, inputs, callback, state); 58 } 59 60 public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) 61 { 62 object returnValue = null; 63 object invokeValue; 64 object[] objArray = new object[0]; 65 System.Exception ex = null; 66 try 67 { 68 invokeValue = this.invoker.InvokeEnd(instance,out outputs,result); 69 returnValue = invokeValue; 70 outputs = objArray; 71 } 72 catch (System.Exception e) 73 { 74 ex = e; 75 returnValue = null; 76 outputs = null; 77 } 78 finally 79 { 80 this.PostInvoke(instance, returnValue, objArray, ex); 81 } 82 return returnValue; 83 } 84 85 public bool IsSynchronous 86 { 87 get { return this.invoker.IsSynchronous; } 88 } 89 90 private void PreInvoke(object instance, object[] inputs) 91 { 92 sw.Start(); 93 } 94 95 private void PostInvoke(object instane, object returnValue, object[] outputs, System.Exception ex) 96 { 97 this.sw.Stop(); 98 if (ex == null)//没有发生异常 99 { 100 WcfGlobalInvocation invocation = new WcfGlobalInvocation(instane,this.operationMethod,this.sw.ElapsedMilliseconds); 101 WcfGlobalEventHelper.FireInvocation(invocation); 102 } 103 else //发生异常 104 { 105 WcfGlobalException we = new WcfGlobalException(ex.Message, ex.StackTrace, instane, this.operationMethod); 106 WcfGlobalEventHelper.FireException(we); 107 throw ex; 108 } 109 } 110 }View Code
这种形式下我们就可以对原有的方法执行过程try.catch,捕获其中的异常。另外此种方式还有一个作用,就是可以监控wcf方法的执行时长,可用于发现服务的性能瓶颈。
其中这两种都用到一个类WcfGlobalEventHelper
1 public delegate void WcfGlobalInvocationEventHandler(WcfGlobalInvocation invocation); 2 public class WcfGlobalEventHelper 3 { 4 //发生异常时 5 private static EventHandlerList listExceptionHandler = new EventHandlerList(); 6 private static readonly object keyException = new object(); 7 8 //未发生异常时 9 private static EventHandlerList listInvocationHandler = new EventHandlerList(); 10 private static readonly object keyInvocation = new object(); 11 12 /// <summary> 13 /// wcf方法发生异常时触发该事件 14 /// </summary> 15 public static event WcfGlobalExceptionEventHandler OnGlobalExceptionExec 16 { 17 add { listExceptionHandler.AddHandler(keyException, value); } 18 remove { listExceptionHandler.RemoveHandler(keyException, value); } 19 } 20 21 public static void FireException(WcfGlobalException ex) 22 { 23 var handler = (WcfGlobalExceptionEventHandler)listExceptionHandler[keyException]; 24 if(handler != null) 25 { 26 handler(ex); 27 } 28 } 29 /// <summary> 30 /// wcf方法调用成功是触发该事件 31 /// </summary> 32 public static event WcfGlobalInvocationEventHandler onGlobalInvocationExec 33 { 34 add { listInvocationHandler.AddHandler(keyInvocation,value);} 35 remove { listInvocationHandler.RemoveHandler(keyInvocation,value);} 36 } 37 38 public static void FireInvocation(WcfGlobalInvocation invocation) 39 { 40 var handler = (WcfGlobalInvocationEventHandler) listInvocationHandler[keyInvocation]; 41 if (handler != null) 42 { 43 handler(invocation); 44 } 45 } 46 }View Code
通过这个类将异常以事件的形式抛出,在服务启动之前为其注册异常处理方法即可。我们一般是发生错误时发送邮件到开发者,这样开发者可以第一时间了解系统的异常,并能快速定位到发生异常的方法以及了解异常产生的大致原因。
下面是示例的完整代码,希望对有需要的朋友有用。这也是本人第一次写博客,有不当的地方还请各位海涵。
http://files.cnblogs.com/icewater506/WcfGlobalException.7z