编写中...........
一、使用
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来预先指定。当然这里就会有很强的耦合,解决方法会在第二种解决方案中给出。
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对异常进行了封装,这样的好处是
二、使用
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#region IErrorHandler Members
public bool HandleError(Exception error)
{
//写入错误日志中
return false;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
ErrorHandlerHelper.PromoteException(error, version, ref fault);
}
#endregion
IServiceBehavior Members#region IServiceBehavior Members
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{ }
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(this);
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{ }
#endregion
}
HandleError方法的调用是通过一个单独的线程来进行的,也就是说你可以在这个地方把错误写入日志,或者传递到别处,即使操作耗时也不会影响到主线程。
ProvideFault方法的调用是用来把一般的异常得升为FaultException和FaultException<T>,这样就不需要在Contract中设置各种不同的FaultException了,注意到方法中调用了
PromoteException方法的封装是来自于<<WCF服务编程>>,最后会以附件的形式给出。
现在,已经得到了
IErrorHandler的实现了,但是如何把它附加到通道中呢,其实也就是简单的在服务实现类中实现
IServiceBehavior就可以在服务初始加载中自动的加载到错误处理器中,我们只需要关于其中的
ApplyDispatchBehavior方法就可以了,例如:
foreach
(ChannelDispatcher dispatcher
in
serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(this);
}
把IServiceBehavior的实现分别添加到服务的通过分发器的错误处理器中。
通过这样的方法可以很容易的把异常发送给客户端了,不过服务器端抛出的异常还是必须封装成FaultException或者FaultException<T>方法的,在客户端也需要捕捉这样的异常,这一点要注意。
ErrorHandlerHelper类的定义如下:
public
static
class
ErrorHandlerHelper
{
public static void PromoteException(Type serviceType, Exception error, MessageVersion version, ref Message fault)
{
//Is error in the form of FaultException<T> ?
if (error.GetType().IsGenericType && error is FaultException)
{
Debug.Assert(error.GetType().GetGenericTypeDefinition() == typeof(FaultException<>));
return;
}
bool inContract = ExceptionInContract(serviceType, error);
if (inContract == false)
{
return;
}
try
{
Type faultUnboundedType = typeof(FaultException<>);
Type faultBoundedType = faultUnboundedType.MakeGenericType(error.GetType());
Exception newException = (Exception)Activator.CreateInstance(error.GetType(), error.Message);
FaultException faultException = (FaultException)Activator.CreateInstance(faultBoundedType, newException);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
}
catch
{ }
}
//Can only be called inside a service
public static void PromoteException(Exception error, MessageVersion version, ref Message fault)
{
StackFrame frame = new StackFrame(1);
Type serviceType = frame.GetMethod().ReflectedType;
PromoteException(serviceType, error, version, ref fault);
}
static bool ExceptionInContract(Type serviceType, Exception error)
{
List<FaultContractAttribute> faultAttributes = new List<FaultContractAttribute>();
Type[] interfaces = serviceType.GetInterfaces();
string serviceMethod = GetServiceMethod(error);
FaultContractAttribute[] attributes;
foreach (Type interfaceType in interfaces)
{
MethodInfo[] methods = interfaceType.GetMethods();
foreach (MethodInfo methodInfo in methods)
{
if (methodInfo.Name == serviceMethod)//Does not deal with overlaoded methods
//or same method name on different contracts implemented explicitly
{
attributes = GetFaults(methodInfo);
faultAttributes.AddRange(attributes);
return FindError(faultAttributes, error);
}
}
}
return false;
}
static string GetServiceMethod(Exception error)
{
const string WCFPrefix = "SyncInvoke";
int start = error.StackTrace.IndexOf(WCFPrefix);
Debug.Assert(start != -1);//Did they change the prefix???
string trimedTillMethod = error.StackTrace.Substring(start + WCFPrefix.Length);
string[] parts = trimedTillMethod.Split('(');
return parts[0];
}
static FaultContractAttribute[] GetFaults(MethodInfo methodInfo)
{
object[] attributes = methodInfo.GetCustomAttributes(typeof(FaultContractAttribute), false);
return attributes as FaultContractAttribute[];
}
static bool FindError(List<FaultContractAttribute> faultAttributes, Exception error)
{
Predicate<FaultContractAttribute> sameFault = delegate(FaultContractAttribute fault)
{
Type detailType = fault.DetailType;
return detailType == error.GetType();
};
return faultAttributes.Exists(sameFault);
}
}
三、自定义ErrorHandlerBehavior特性
参考:
<<WCF服务编程>>
WCF中的异常处理
补充:如何设定includeExceptionDetailInFaults?
1、使用
ServiceBehaviorAttribute
[ServiceBehavior(IncludeExceptionDetailInFaults
=
true
)]
public class SpecialProvider : ISpecialProvider
2、编码设定
ServiceDebugBehavior debug
=
serviceHost.Description.Behaviors.Find
<
ServiceDebugBehavior
>
();
debug.IncludeExceptionDetailInFaults
=
true
;
3、写配置文件
<?
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
>