在用wcf做為單純的服務端的時候,發生錯誤是常有的事情,特别是在調用其他系統提供的接口的時候,發生的一些錯誤總是讓人摸不着頭腦,嚴重影響了錯誤的定位。做.net web開發的時候,我們可以在Global裡面直接捕獲全局異常,那麼wcf是否也可以定義全局異常處理?對于已有的系統,逐個方法添加異常處理是很不現實而且還會伴随很大的風險,那麼我們肯定希望這種改動盡可能的小甚至不用改動。下面分享一下實作的方法:
利用Attribure和IServiceBehavior實作wcf全局異常處理
這種方式使用友善,基本不用改動原有的代碼,效果如下:

1 [WcfGlobalExceptionOpreationBehaviorAttribute(typeof(WcfGlobalErrorHandler))]
2 public class DemoService : IDemoService
3 {
4 }
View Code
或者

1 [WcfGlobalServiceInterceptor]
2 public class DemoService : IDemoService
3 {
4 }
以上的兩種方式在具體實作上稍有不同,具體實作方式如下:
在此之前先來看看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 }
原理就是我們為信道分發器注入自定義的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 }
形式二: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 }
實際上就是在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 }
這種形式下我們就可以對原有的方法執行過程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 }
通過這個類将異常以事件的形式抛出,在服務啟動之前為其注冊異常處理方法即可。我們一般是發生錯誤時發送郵件到開發者,這樣開發者可以第一時間了解系統的異常,并能快速定位到發生異常的方法以及了解異常産生的大緻原因。
下面是示例的完整代碼,希望對有需要的朋友有用。這也是本人第一次寫部落格,有不當的地方還請各位海涵。
https://files.cnblogs.com/icewater506/WcfGlobalException.7z