一、使用FaultContract
1、WCF通过提供FaultContract特性指明操作可能会抛出的异常类型,只有通过该特性指明的类型才会有可能在服务调用出现异常时把该类型序列化传递给调用方。例如定议了一个Contract如下:
[ServiceContract]
public interface IRequestClient
{
[OperationContract]
[FaultContract(typeof(System.Exception))]
[FaultContract(typeof(System.Net.WebException))]
[FaultContract(typeof(ExceptionDetail))]
string RequestContent(string clientID, string sourceID, string handleMode, string identity);
}
方法RequestContent可能在调用过程中会抛出的各种异常,但是需要通过FaultContract来预先指定。当然这里就会有很强的耦合,解决方法会在第二种解决方案中给出。public interface IRequestClient
{
[OperationContract]
[FaultContract(typeof(System.Exception))]
[FaultContract(typeof(System.Net.WebException))]
[FaultContract(typeof(ExceptionDetail))]
string RequestContent(string clientID, string sourceID, string handleMode, string identity);
}
2、虽然指定了RequestContent特性,但是但如果服务抛出的异常并不是FaultException或者FaultException<T>异常,会异常WCF的通信通导发生错误从而使得抛出CommunicationObjectFaultedException异常,无法继续使用服务。所以在实际的服务中可以把你想要传递的异常封装为FaultException或者FaultException<T>类型抛出,然后在客户端截获这种类型。使用ActionClientAccept是IClientAccept的实现,它抛出的异常就是经过封装的。
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, IncludeExceptionDetailInFaults = true)]
public class ActionClientAccept : IClientAccept
{
public bool AcceptContent(string clientID, string sourceID, string handleMode, string identity, string content)
{
try
{
throw new NotImplementedException("测试");
}
catch (Exception ex)
{
ExceptionDetail detail = new ExceptionDetail(ex);
throw new FaultException<ExceptionDetail>(detail, ex.Message);
}
}
}
注意:这里我们还使用了一个ExceptionDetail对异常进行了封装,这样的好处是public class ActionClientAccept : IClientAccept
{
public bool AcceptContent(string clientID, string sourceID, string handleMode, string identity, string content)
{
try
{
throw new NotImplementedException("测试");
}
catch (Exception ex)
{
ExceptionDetail detail = new ExceptionDetail(ex);
throw new FaultException<ExceptionDetail>(detail, ex.Message);
}
}
}
二、使用IErrorHandler和IServiceBehavior接口实现错误处理扩展。
服务的实现类ActionClientAccept通过实现IErrorHandler接口,可以把一般的异常提升为FaultException或者FaultException<T>异常,通过这样的方式,就可以不需要在IRequestClient中定义FaultContract特性,从而实现了一定程度的解藕,例子如下:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, IncludeExceptionDetailInFaults = true)]
public class ActionClientAccept : IClientAccept, IErrorHandler, IServiceBehavior
{
public bool AcceptContent(string clientID, string sourceID, string handleMode, string identity, string content)
{
throw new NotImplementedException();
}
IErrorHandler Members
IServiceBehavior Members
}
HandleError方法的调用是通过一个单独的线程来进行的,也就是说你可以在这个地方把错误写入日志,或者传递到别处,即使操作耗时也不会影响到主线程。public class ActionClientAccept : IClientAccept, IErrorHandler, IServiceBehavior
{
public bool AcceptContent(string clientID, string sourceID, string handleMode, string identity, string content)
{
throw new NotImplementedException();
}
IErrorHandler Members
IServiceBehavior Members
}
ProvideFault方法的调用是用来把一般的异常得升为FaultException和FaultException<T>,这样就不需要在Contract中设置各种不同的FaultException了,注意到方法中调用了PromoteException方法的封装是来自于<<WCF服务编程>>,最后会以附件的形式给出。
现在,已经得到了IErrorHandler的实现了,但是如何把它附加到通道中呢,其实也就是简单的在服务实现类中实现IServiceBehavior就可以在服务初始加载中自动的加载到错误处理器中,我们只需要关于其中的ApplyDispatchBehavior方法就可以了,例如:
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(this);
}
把IServiceBehavior的实现分别添加到服务的通过分发器的错误处理器中。{
dispatcher.ErrorHandlers.Add(this);
}
通过这样的方法可以很容易的把异常发送给客户端了,不过服务器端抛出的异常还是必须封装成FaultException或者FaultException<T>方法的,在客户端也需要捕捉这样的异常,这一点要注意。
ErrorHandlerHelper类的定义如下:
public static class ErrorHandlerHelper
三、自定义ErrorHandlerBehavior特性
参考:
<<WCF服务编程>>
WCF中的异常处理
补充:如何设定includeExceptionDetailInFaults?
1、使用ServiceBehaviorAttribute
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
2、编码设定public class SpecialProvider : ISpecialProvider
ServiceDebugBehavior debug = serviceHost.Description.Behaviors.Find<ServiceDebugBehavior>();
debug.IncludeExceptionDetailInFaults = true;
3、写配置文件debug.IncludeExceptionDetailInFaults = true;
<?xml version="1.0" encoding="utf-8" ?>
<system.serviceModel>
<services>
<service name="XXXService" behaviorConfiguration="XXXServiceBehavior"></services>
<behaviors>
<serviceBehaviors>
<behavior name="XXXServiceBehavior">
<serviceDebug httpHelpPageEnabled="True" includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.serviceModel>
<services>
<service name="XXXService" behaviorConfiguration="XXXServiceBehavior"></services>
<behaviors>
<serviceBehaviors>
<behavior name="XXXServiceBehavior">
<serviceDebug httpHelpPageEnabled="True" includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>