在用wcf作爲單純的服務端的時候,發生錯誤是常有的事情,特別是在調用其餘系統提供的接口的時候,發生的一些錯誤老是讓人摸不着頭腦,嚴重影響了錯誤的定位。作.net web開發的時候,咱們能夠在Global裏面直接捕獲全局異常,那麼wcf是否也能夠定義全局異常處理?對於已有的系統,逐個方法添加異常處理是很不現實並且還會伴隨很大的風險,那麼咱們確定但願這種改動儘量的小甚至不用改動。下面分享一下實現的方法:html
利用Attribure和IServiceBehavior實現wcf全局異常處理web
這種方式使用方便,基本不用改動原有的代碼,效果以下:數組
1 [WcfGlobalExceptionOpreationBehaviorAttribute(typeof(WcfGlobalErrorHandler))] 2 public class DemoService : IDemoService 3 { 4 }
或者ide
1 [WcfGlobalServiceInterceptor] 2 public class DemoService : IDemoService 3 { 4 }
以上的兩種方式在具體實現上稍有不一樣,具體實現方式以下:
在此以前先來看看IServiceBehavior接口提供的方法post
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 this
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 }
原理就是咱們爲信道分發器注入自定義的ErrorHandlerurl
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 }
形式二:IOperationBehaviorspa
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 }
實際上就是在ApplyDispatchBehavior方法中分發操做的Invoker進行了封裝.net
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 }
這種形式下咱們就能夠對原有的方法執行過程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 }
經過這個類將異常以事件的形式拋出,在服務啓動以前爲其註冊異常處理方法便可。咱們通常是發生錯誤時發送郵件到開發者,這樣開發者能夠第一時間瞭解系統的異常,並能快速定位到發生異常的方法以及瞭解異常產生的大體緣由。
下面是示例的完整代碼,但願對有須要的朋友有用。這也是本人第一次寫博客,有不當的地方還請各位海涵。