在用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