在用wcf做为单纯的服务端的时候,发生错误是常有的事情,特别是在调用其他系统提供的接口的时候,发生的一些错误总是让人摸不着头脑,严重影响了错误的定位。做.net web开发的时候,我们可以在Global里面直接捕获全局异常,那么wcf是否也可以定义全局异常处理?对于已有的系统,逐个方法添加异常处理是很不现实而且还会伴随很大的风险,那么我们肯定希望这种改动尽可能的小甚至不用改动。下面分享一下实现的方法:

利用Attribure和IServiceBehavior实现wcf全局异常处理

 这种方式使用方便,基本不用改动原有的代码,效果如下:

 [WcfGlobalExceptionOpreationBehaviorAttribute(typeof(WcfGlobalErrorHandler))]
public class DemoService : IDemoService
{
}

或者

 [WcfGlobalServiceInterceptor]
public class DemoService : IDemoService
{
}

以上的两种方式在具体实现上稍有不同,具体实现方式如下:
在此之前先来看看IServiceBehavior接口提供的方法

 public interface IServiceBehavior
{
void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters);
void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
}

后面我们用到的是方法ApplyDispatchBehavior,这个方法会在服务Open的时候执行,我们可以在此注入我们自定义的异常处理程序,从而达到处理全局异常的目的(注:试验证明在服务Open之后再注入分发行为是无效的)。

形式一:IErrorHandler

 private readonly Type _errorHandlerType;
public WcfGlobalExceptionOpreationBehaviorAttribute(Type handlerType)
{
_errorHandlerType = handlerType;
} /// <summary>
/// 注入自定义异常处理程序
/// </summary>
/// <param name="serviceDescription"></param>
/// <param name="serviceHostBase"></param>
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
var handler = (IErrorHandler) Activator.CreateInstance(_errorHandlerType);
foreach (ChannelDispatcher dis in serviceHostBase.ChannelDispatchers)
{
dis.ErrorHandlers.Add(handler);
}
}

原理就是我们为信道分发器注入自定义的ErrorHandler

 /// <summary>
/// WCF全局异常处理
/// </summary>
public class WcfGlobalErrorHandler:IErrorHandler
{
/// <summary>
/// 启用错误相关处理并返回一个值
/// </summary>
/// <param name="ex"></param>
/// <returns>是否终止会话和上下文</returns>
public bool HandleError(System.Exception ex)
{
WcfGlobalEventHelper.FireException(new WcfGlobalException(ex.Message,ex.StackTrace,null,null));
return true;
} public void ProvideFault(System.Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
{ } }

形式二:IOperationBehavior

     public class WcfGlobalOperationInterceptorAttribute:Attribute,IOperationBehavior
{
private string operationMethod; public WcfGlobalOperationInterceptorAttribute(string methodName)
{
this.operationMethod = methodName;
} public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{ } public void ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation)
{ } public void ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
{
IOperationInvoker invoke = dispatchOperation.Invoker;
dispatchOperation.Invoker = CreateInvoker(invoke);
} public void Validate(OperationDescription operationDescription)
{ } protected OperationInvoker CreateInvoker(IOperationInvoker oldInvoker)
{
return new OperationInvoker(oldInvoker, this.operationMethod);
}
}

实际上就是在ApplyDispatchBehavior方法中分发操作的Invoker进行了封装

 /// <summary>
/// 使用消息提取的对象以及参数数组,并利用对该对象调用方法,然后返回该方法的返回值和输出参数
/// </summary>
public class OperationInvoker:IOperationInvoker
{
private string operationMethod;//方法名称
private IOperationInvoker invoker;//原invoker
private Stopwatch sw;//用于计时 public OperationInvoker(IOperationInvoker oldInvoker, string operationMethod)
{
this.invoker = oldInvoker;
this.operationMethod = operationMethod;
sw = new Stopwatch();
} public object[] AllocateInputs()
{
return this.invoker.AllocateInputs();
}
/// <summary>
/// 从一个实例和输入对象的集合返回一个对象和输出对象的集合
/// </summary>
/// <param name="instance"></param>
/// <param name="inputs"></param>
/// <param name="outputs"></param>
/// <returns></returns>
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
this.PreInvoke(instance, inputs);
object returnValue = null;
object invokeValue;
object[] objArray = new object[];
System.Exception ex = null;
try
{
invokeValue = this.invoker.Invoke(instance, inputs, out objArray);
returnValue = invokeValue;
outputs = objArray;
}
catch (System.Exception e)
{
ex = e;
returnValue = null;
outputs = null;
}
finally
{
this.PostInvoke(instance,returnValue,objArray,ex);
}
return returnValue;
} public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
this.PreInvoke(instance,inputs);
return this.invoker.InvokeBegin(instance, inputs, callback, state);
} public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
object returnValue = null;
object invokeValue;
object[] objArray = new object[];
System.Exception ex = null;
try
{
invokeValue = this.invoker.InvokeEnd(instance,out outputs,result);
returnValue = invokeValue;
outputs = objArray;
}
catch (System.Exception e)
{
ex = e;
returnValue = null;
outputs = null;
}
finally
{
this.PostInvoke(instance, returnValue, objArray, ex);
}
return returnValue;
} public bool IsSynchronous
{
get { return this.invoker.IsSynchronous; }
} private void PreInvoke(object instance, object[] inputs)
{
sw.Start();
} private void PostInvoke(object instane, object returnValue, object[] outputs, System.Exception ex)
{
this.sw.Stop();
if (ex == null)//没有发生异常
{
WcfGlobalInvocation invocation = new WcfGlobalInvocation(instane,this.operationMethod,this.sw.ElapsedMilliseconds);
WcfGlobalEventHelper.FireInvocation(invocation);
}
else //发生异常
{
WcfGlobalException we = new WcfGlobalException(ex.Message, ex.StackTrace, instane, this.operationMethod);
WcfGlobalEventHelper.FireException(we);
throw ex;
}
}
}

这种形式下我们就可以对原有的方法执行过程try.catch,捕获其中的异常。另外此种方式还有一个作用,就是可以监控wcf方法的执行时长,可用于发现服务的性能瓶颈。

其中这两种都用到一个类WcfGlobalEventHelper

 public delegate void WcfGlobalInvocationEventHandler(WcfGlobalInvocation invocation);
public class WcfGlobalEventHelper
{
//发生异常时
private static EventHandlerList listExceptionHandler = new EventHandlerList();
private static readonly object keyException = new object(); //未发生异常时
private static EventHandlerList listInvocationHandler = new EventHandlerList();
private static readonly object keyInvocation = new object(); /// <summary>
/// wcf方法发生异常时触发该事件
/// </summary>
public static event WcfGlobalExceptionEventHandler OnGlobalExceptionExec
{
add { listExceptionHandler.AddHandler(keyException, value); }
remove { listExceptionHandler.RemoveHandler(keyException, value); }
} public static void FireException(WcfGlobalException ex)
{
var handler = (WcfGlobalExceptionEventHandler)listExceptionHandler[keyException];
if(handler != null)
{
handler(ex);
}
}
/// <summary>
/// wcf方法调用成功是触发该事件
/// </summary>
public static event WcfGlobalInvocationEventHandler onGlobalInvocationExec
{
add { listInvocationHandler.AddHandler(keyInvocation,value);}
remove { listInvocationHandler.RemoveHandler(keyInvocation,value);}
} public static void FireInvocation(WcfGlobalInvocation invocation)
{
var handler = (WcfGlobalInvocationEventHandler) listInvocationHandler[keyInvocation];
if (handler != null)
{
handler(invocation);
}
}
}

通过这个类将异常以事件的形式抛出,在服务启动之前为其注册异常处理方法即可。我们一般是发生错误时发送邮件到开发者,这样开发者可以第一时间了解系统的异常,并能快速定位到发生异常的方法以及了解异常产生的大致原因。

下面是示例的完整代码,希望对有需要的朋友有用。这也是本人第一次写博客,有不当的地方还请各位海涵。

http://files.cnblogs.com/icewater506/WcfGlobalException.7z

WCF全局异常处理的更多相关文章

  1. 服务端增加WCF服务全局异常处理机制

    服务端增加WCF服务全局异常处理机制,任一WCF服务或接口方式出现异常,将统一调用WCF_ExceptionHandler.ProvideFault方法,因此不需要每个方法使用try catch写法. ...

  2. WCF服务全局异常处理机制

    服务端增加WCF服务全局异常处理机制,任一WCF服务或接口方式出现异常,将统一调用WCF_ExceptionHandler.ProvideFault方法,因此不需要每个方法使用try catch写法. ...

  3. mvc自定义全局异常处理

    异常信息处理是任何网站必不可少的一个环节,怎么有效显示,记录,传递异常信息又成为重中之重的问题.本篇将基于上篇介绍的html2cancas截图功能,实现mvc自定义全局异常处理.先看一下最终实现效果: ...

  4. 在.NET Core程序中设置全局异常处理

    以前我们想设置全局异常处理只需要这样的代码: AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.UnhandledExc ...

  5. springMvc全局异常处理

    本文中只测试了:实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器 对已有代码没有入侵性等优点,同时,在异常处理时能获取导致出现异常的对象,有利于提 ...

  6. WCF初探-12:WCF客户端异常处理

    前言: 当我们打开WCF基础客户端通道(无论是通过显式打开还是通过调用操作自动打开).使用客户端或通道对象调用操作,或关闭基础客户端通道时,都会在客户端应用程序中出现异常.而我们知道WCF是基于网络的 ...

  7. MVC 全局异常处理及禁用显示头

    MVC网站的global.asax中的Application_Start方法里,有这样一段代码: public class MvcApplication : System.Web.HttpApplic ...

  8. WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]

    原文:WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇] 在[上篇]中,我们分别站在消息交换和编程的角度介绍了SOAP Fault和FaultException异常.在服务执行过 ...

  9. WCF技术剖析之二十一:WCF基本异常处理模式[下篇]

    原文:WCF技术剖析之二十一:WCF基本异常处理模式[下篇] 从FaultContractAttribute的定义我们可以看出,该特性可以在同一个目标对象上面多次应用(AllowMultiple = ...

随机推荐

  1. 解决android studio设置版本号

    获取版本号代码 /** * 获取版本号 * @return 当前应用的版本号 */ public static String getVersion(Context context) { try { P ...

  2. 校内选拔I题题解 构造题 Codeforces Round #318 [RussianCodeCup Thanks-Round] (Div. 2) ——D

    http://codeforces.com/contest/574/problem/D Bear and Blocks time limit per test 1 second memory limi ...

  3. HDOJ4550 卡片游戏 随便销毁内存的代价就是wa//string类的一些用法

    思路 标记最小的最后的位置  放在第一位 标记位置之前按left值小的左方大的右方 标记位置之后按顺序放在最后 不多说先贴上销毁内存的wa代码 销毁内存的wa代码 #include<cstdio ...

  4. 第009课 gcc和arm-linux-gcc和MakeFile

    from:第009课 gcc和arm-linux-gcc和MakeFile 第001节_gcc编译器1_gcc常用选项_gcc编译过程详解 gcc的使用方法 gcc [选项] 文件名 gcc常用选项 ...

  5. 【C语言项目】贪吃蛇游戏(下)

    目录 00. 目录 07. 游戏逻辑 7.5 按下ESC键结束游戏 7.6 判断是否撞到墙 7.7 判断是否咬到自己 08. 游戏失败界面设计 8.1 游戏失败界面边框设计 8.2 撞墙失败界面 8. ...

  6. helm istio k8s docker

    helm https://hub.helm.sh/ k8s https://www.kubernetes.org.cn/k8s istio 微服务 https://istio.io/

  7. 题解 P1189 SEARCH

    (传送门)[https://www.luogu.org/problemnew/show/P1189] 先反省一波:我以后再也不用getchar()+scanf了(日常爆零) 算是比较裸的搜索吧,在下用 ...

  8. js函数式编程(三)-compose和pointFree

    compose即函数嵌套组合 组合compose在第一篇已经初见端倪,可以感受一下.compose函数的实现用闭包的方法.不完善实现如下: const compose = (f, g) => { ...

  9. awk纯干货

    AWK的惊人表现: Awk设计的目的:简化一般文本处理的工作. 属于POSIX的一部分. AWK命令行: Awk的调用可以定义变量.提供程序并且指定输入文件: Awk [ -F fs ]  [ -v ...

  10. 学习笔记(_huaji_)

    假如我没有见过太阳,我也许会忍受黑暗. 如果我知道自己会在哪里死去,我就永远都不去那儿.失败的经历,其实也有它的价值. 人的过失会带来错误,但要制造真正的灾难还得用计算机. 嘴角微微上扬已不复当年轻狂 ...