对于WCF中的异常与错误处理我们必须先了解一个概念SOAP Faults,它实际上就是在服务端和客户端之间来传递错误信息的一种载体。
• 公共语言运行时(CLR)异常无法跨越服务边界
– 未捕捉异常最多到达服务通道(service channel)
– 在报告给客户端之前必须要进行序列化
• 所有的异常都被序列化为SOAP faults
– 基于标准的,可互操作的
– 给予SOAP 1.1 或者 SOAP 1.2 格式
SOAP1.1
xmlns:s ="http://schemas.xmlsoap.org/soap/envelope/" >
< s:Body >
< s:Fault >
< faultcode xmlns ="" > s:Client </ faultcode >
< faultstring xml:lang ="en-US" xmlns ="" >
An invalid operation has occurred.
</ faultstring >
</ s:Fault >
</ s:Body >
</ s:Envelope >
SOAP1.2
xmlns:a ="http://www.w3.org/2005/08/addressing" >
< s:Header >
< a:Action s:mustUnderstand ="1" >
http://www.w3.org/2005/08/addressing/soap/fault
</ a:Action >
< a:RelatesTo >
urn:uuid:64c5619c-99c3-4a83-9bdcfcbb6f399f93 </
a:RelatesTo >
</ s:Header >
< s:Body >
< s:Fault >
< s:Code >
< s:Value > s:Sender </ s:Value >
</ s:Code >
< s:Reason >
< s:Text xml:lang ="en-US" >
An invalid operation
has occurred.
</ s:Text >
</ s:Reason >
</ s:Fault >
</ s:Body >
</ s:Envelope >
非捕捉的异常
• CLR异常会从业务逻辑组件传递到服务层
• 在缺省状态下,异常的细节不会与客户端应用程序共享
• 返回一个通用的SOAP Fault
抛出异常
• 考虑下面这个CLR异常:
throw new InvalidOperationException("The method or operation is not implemented.");
• 如果没有被捕捉到,服务模型将会返回一个SOAP Fault
– 缺省情况下,异常中不包含细节信息
– 能够选择添加细节信息
• SOAP 格式依赖于绑定(binding)
IncludeExceptionDetailsInFaults(打开开关[true]就可以看到具体的异常信息了)
• IncludeExceptionDetailsInFaults
– Debugging行为控制如何对待未捕捉的异常
– 如果被打开,生成的SOAP fault中,异常细节部分将包括栈跟踪的信息
• 发送调用栈跟踪细节是有风险的
• 只用于调试目的
在<serviceDebug>区域中配置服务的行为:
< services >
< service name ="ExceptionService.Service"
behaviorConfiguration ="serviceBehavior" >
< endpoint address ="http://localhost:8000/Service"
contract ="ExceptionService.IService" binding ="wsHttpBinding" />
</ service >
</ services >
< behaviors >
< serviceBehaviors >
< behavior name ="serviceBehavior" >
< serviceDebug includeExceptionDetailInFaults ="true" />
</ behavior >
</ serviceBehaviors >
</ behaviors >
</ system.serviceModel >
使用ServiceBehaviorAttribute来初始化行为:(code方式)
[ServiceBehaviorAttribute(IncludeExceptionDetailsInFaults=true)]
public class Service : IService
下面看一个Demo:
先提出一个知识点:
[OperationContract(IsOneWay=true)]的含义。
IsOneWay=false是默认的设置,是一种阻塞方式的调用,可以理解为同步调用,当客户端发送调用服务的请求后,客户端被阻塞,直到收到服务端处理的返回结果才继续工作,我们绝大多数的程序都是这种方式。
IsOneWay=true和异步调用方式类似,但是不完全相同。还是有区别的:纯异步的调用方式,当客户端发出调用请求后,客户端不会被阻塞,而是可以继续工作,此时客户端不会理会请求的何时被处理了,或者说是不是会被WCF处理。OneWay的话是这样的,只有当WCF通知了客户端准备开始处理请求了此时客户端才可以继续工作,不被阻塞;但是如果客户端提交的请求一直不被WCF处理,处于挂起状态的话,此时客户端会处于阻塞状态。
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace BusinessServices
{
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in App.config.
[ServiceContract]
public interface IMyServiceA
{
[OperationContract]
void ThrowException();
[OperationContract(IsOneWay = true )]
void ThrowExceptionOneWay();
}
}
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace BusinessServices
{
// NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in App.config.
public class MyService : IMyServiceA
{
public void ThrowException()
{
throw new Exception( " This Exception is thrown by Charles " );
}
public void ThrowExceptionOneWay()
{
throw new Exception( " This Exception is thrown by Charles " );
}
}
}
< configuration >
< system.web >
< compilation debug ="true" />
</ system.web >
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
< system.serviceModel >
< services >
< service behaviorConfiguration ="BusinessServices.Service1Behavior"
name ="BusinessServices.MyService" >
< endpoint address ="" binding ="wsHttpBinding" contract ="BusinessServices.IMyServiceA" >
< identity >
< dns value ="localhost" />
</ identity >
</ endpoint >
< endpoint address ="mex" binding ="mexHttpBinding" contract ="IMetadataExchange" />
< host >
< baseAddresses >
< add baseAddress ="http://localhost:8731/WCF/Charlesliu" />
</ baseAddresses >
</ host >
</ service >
</ services >
< behaviors >
< serviceBehaviors >
< behavior name ="BusinessServices.Service1Behavior" >
<!-- To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint above before deployment -->
< serviceMetadata httpGetEnabled ="True" />
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
< serviceDebug includeExceptionDetailInFaults ="true" />
</ behavior >
</ serviceBehaviors >
</ behaviors >
</ system.serviceModel >
</ configuration >
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace ClientTest
{
class Program
{
static void Main( string [] args)
{
MyServiceReference.MyServiceAClient proxy = new MyServiceReference.MyServiceAClient();
try
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine( " Calling proxy.ThrowException() " );
Console.WriteLine( "" );
Console.ResetColor();
proxy.ThrowException();
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().ToString());
Console.WriteLine( " ERROR: {0} " , ex.Message);
}
Console.WriteLine();
Console.WriteLine( " Proxy state after exception: {0} " , proxy.State);
Console.WriteLine();
if (proxy.State == CommunicationState.Faulted)
proxy = new MyServiceReference.MyServiceAClient();
try
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine( " Calling proxy.ThrowExceptionOneWay() " );
Console.ResetColor();
proxy.ThrowExceptionOneWay();
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().ToString());
Console.WriteLine( " ERROR: {0} " , ex.Message);
}
Console.WriteLine();
Console.WriteLine( " Proxy state after exception: {0} " , proxy.State);
Console.WriteLine();
try
{
proxy.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().ToString());
Console.WriteLine( " ERROR: {0} " , ex.Message);
}
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine( " Press <ENTER> to terminate Client. " );
Console.ReadLine();
}
}
}
运行后:
可以看到当<serviceDebug includeExceptionDetailInFaults="true" />时,客户端可以看到具体的Exception信息了,否则只能看到一个通用的信息。对于OnWay的调用Exception是没有显示出来的,因为在Exception还没有传到客户端之前,客户端代码继续往下运行了,这就是OneWay在try-catch中特点的体现。我们还可以发现当WCF服务有异常发生时,proxy实例的state会发生变化,所以在编程中要考虑怎样处理。
抛出Fault(Fault指的是SOAP的fault,这和.net的Exception是两个不同的概念):他的意思是把Fault作为数据对象发送到客户端,他的主要作用就是对异常做个包装。
• 未捕获异常表明服务模型发生了一个潜在的致命错误
• 类型:
– FaultException
– FaultException<T>
– MessageFault
• 用于抛出简单的错误
• 可以提供错误原因与代码
• 能够提供额外的SOAP错误元素
throw new FaultException( new FaultReason( " An invalid operation has occurred. " ));
throw new FaultException( new FaultReason( " An invalid operation has occurred. " ), FaultCode.CreateSenderFaultCode( null ));
• 为SOAP fault提供一个强类型的<T>的简单方法
– T 必须是数据契约或者可序列化类型
• 也能够使用CLR异常类型
– 不利于互操作
• 为服务错误创建数据契约
–更好地用于互操作
" http://www.thatindigogirl.com/samples/2006/06 " )]
public interface IPhotoUpload
{
[OperationContract]
[FaultContract( typeof (ReceiverFault))]
[FaultContract( typeof (SenderFault))]
void UploadPhoto(PhotoLink fileInfo, byte [] fileData);
}
" http://schemas.thatindigogirl.com/samples/2006/06 " )]
public class ReceiverFault
{
private string m_message;
private string m_description;
[DataMember(Name = " Message " , IsRequired = true , Order = 0 )]
public string Message
{
get { return m_message; }
set { m_message = value; }
}
[DataMember(Name = " Description " , IsRequired = false , Order = 1 )]
public string Description
{
get { return m_description; }
set { m_description = value; }
}
}
[DataContract(Namespace = " http://schemas.thatindigogirl.com/samples/2006/06 " )]
public class SenderFault
{
private string m_message;
private string m_description;
private List < string > m_failedBodyElements = new List < string > ();
[DataMember(Name = " Message " , IsRequired = true , Order = 0 )]
public string Message
{
get { return m_message; }
set { m_message = value; }
}
[DataMember(Name = " Description " , IsRequired = false , Order = 1 )]
public string Description
{
get { return m_description; }
set { m_description = value; }
}
[DataMember(Name = " FailedBodyElements " , IsRequired = true , Order = 2 )]
public List < string > FailedBodyElements
{
get { return m_failedBodyElements; }
set { m_failedBodyElements = value; }
}
}
• SOAP Fault的CLR表示
–为了更好地控制错误元素
FaultCode.CreateSenderFaultCode( null ),
new FaultReason( " Invalid operation " ),
new InvalidOperationException( " An invalid operation has
occurred. " ), null, "" , "" );
FaultException fe = FaultException.CreateFault(mfault,
typeof (InvalidOperationException));
throw fe;
看一个Demo:
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace BusinessServices
{
[ServiceContract]
public interface IFaultExceptionService
{
[OperationContract]
[FaultContract( typeof (InvalidOperationException))]
void ThrowSimpleFault();
[OperationContract]
[FaultContract( typeof (InvalidOperationException))]
void ThrowMessageFault();
[OperationContract()]
[FaultContract( typeof (InvalidOperationException))]
void ThrowFaultException();
}
}
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Channels;
namespace BusinessServices
{
public class FaultExceptionService : IFaultExceptionService
{
#region IService Members
public void ThrowSimpleFault()
{
throw new FaultException( " An invalid operation has occurred. " );
}
public void ThrowMessageFault()
{
InvalidOperationException error = new InvalidOperationException( " An invalid operation has occurred. " );
MessageFault mfault = MessageFault.CreateFault( new FaultCode( " Server " , new FaultCode(String.Format( " Server.{0} " , error.GetType().Name))), new FaultReason(error.Message), error);
FaultException fe = FaultException.CreateFault(mfault, typeof (InvalidOperationException));
throw fe;
}
public void ThrowFaultException()
{
FaultException < InvalidOperationException > fe = new FaultException < InvalidOperationException > ( new InvalidOperationException( " An invalid operation has occured. " ), " Invalid operation. " , new FaultCode( " Server " , new FaultCode(String.Format( " Server.{0} " , typeof (NotImplementedException)))));
throw fe;
}
#endregion
}
}
< configuration >
< system.web >
< compilation debug ="true" />
</ system.web >
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
< system.serviceModel >
< services >
< service behaviorConfiguration ="BusinessServices.Service1Behavior"
name ="BusinessServices.FaultExceptionService" >
< endpoint address ="" binding ="wsHttpBinding" contract ="BusinessServices.IFaultExceptionService" >
< identity >
< dns value ="localhost" />
</ identity >
</ endpoint >
< endpoint address ="mex" binding ="mexHttpBinding" contract ="IMetadataExchange" />
< host >
< baseAddresses >
< add baseAddress ="http://localhost:8731/WCF/Charlesliu" />
</ baseAddresses >
</ host >
</ service >
</ services >
< behaviors >
< serviceBehaviors >
< behavior name ="BusinessServices.Service1Behavior" >
<!-- To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint above before deployment -->
< serviceMetadata httpGetEnabled ="True" />
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
< serviceDebug includeExceptionDetailInFaults ="false" />
</ behavior >
</ serviceBehaviors >
</ behaviors >
</ system.serviceModel >
</ configuration >
注意:includeExceptionDetailInFaults="false"
Client代码:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace ClientTest
{
class Program
{
static void Main( string [] args)
{
MyServiceReference.FaultExceptionServiceClient proxy = new ClientTest.MyServiceReference.FaultExceptionServiceClient();
try
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine( " Calling proxy.ThrowSimpleFault() " );
Console.WriteLine( "" );
Console.ResetColor();
proxy.ThrowSimpleFault();
}
catch (FaultException fe)
{
Console.WriteLine(fe.GetType().ToString());
Console.WriteLine( " ERROR: {0} " , fe.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().ToString());
Console.WriteLine( " ERROR: {0} " , ex.Message);
}
Console.WriteLine();
try
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine( " Calling proxy.ThrowMessageFault() " );
Console.ResetColor();
proxy.ThrowMessageFault();
}
catch (FaultException fe)
{
Console.WriteLine(fe.GetType().ToString());
Console.WriteLine( " ERROR: {0} " , fe.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().ToString());
Console.WriteLine( " ERROR: {0} " , ex.Message);
}
Console.WriteLine();
try
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine( " Calling proxy.ThrowFaultException() " );
Console.ResetColor();
proxy.ThrowFaultException();
}
catch (FaultException fe)
{
Console.WriteLine(fe.GetType().ToString());
Console.WriteLine( " ERROR: {0} " , fe.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().ToString());
Console.WriteLine( " ERROR: {0} " , ex.Message);
}
proxy.Close();
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine( " Press <ENTER> to terminate Client. " );
Console.ReadLine();
}
}
}
运行结果:
Fault声明 (其实上面的例子已经给出了声明的方法)
• 缺省情况下,客户端不会意识到可能抛出的错误操作
• Fault可以作为WSDL的一部分进行声明
–描述<T> 元素
– 生成包含可适用类型信息的代理
• 提供带有强类型异常的客户端(这个好处是可以根据特定的异常,进行相应的处理,比如:内存异常,IO异常,网络异常,进行相应的逻辑处理)
FaultContractAttribute
• 应用于服务契约或者操作
• 为Fault细节提供数据契约或者可序列化类型
• 操作应该抛出声明的Fault
Fault声明的方法
• 使用CLR异常提供细节信息
• 定义一些带有特性规范的核心数据契约
– ReceiverFault
– SenderFault
• 为Fault的每个特定类型定义数据契约
– FileUploadFault
– CustomerDataFault
也就是说上例中的服务端定义的具体Fault和客户端对应起来是一种好的方法,如服务端声明的是InvalidOperationException的Fault,客户款的catch改为: catch (FaultException<InvalidOperationException> fe)。
实现错误处理逻辑
• WCF支持集中化错误处理
–报告未捕捉的异常
– 将适当的异常转换为Fault
–修改Fault保证一致性
• 为IErrorHandler提供实现
• 添加到配置好的服务行为(service behaviors)中
• IErrorHandler 方法:作用就是实现了把CLR异常到Fault的包装
– ProvideFault() (这个方法的执行是在WCFServer端发生异常和WCFserver把异常传递给客户端这两个阶段之间。)
• 在发生异常后,异常消息返回并且终止会话前被调用
• 用于修改和包装返回的异常消息
• 客户端处于阻塞状态
– 不要在其内部做长时间的处理,以免客户端超时
– HandleError() (这个方法当异常返回给客户端后,在服务端被触发)
• 在异常返回给客户端之后被触发
• 不会阻塞通讯
• 通常用于记录异常,在服务端进行错误提示等操作
具体的看一个Demo:
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WcfServiceLibrary1
{
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in App.config.
[ServiceContract]
public interface IMyService
{
[OperationContract]
[FaultContract( typeof (ReceiverFaultDetail))]
[FaultContract( typeof (SenderFaultDetail))]
void GetData( int flag);
}
}
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Description;
using System.Collections.ObjectModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
namespace WcfServiceLibrary1
{
// NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in App.config.
public class MyService : IMyService
{
public void GetData( int flag)
{
if (flag == 0 )
{
throw new InvalidOperationException( " error - InvalidOperationException " );
}
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
namespace WcfServiceLibrary1
{
[DataContract]
public class ReceiverFaultDetail
{
private string m_message;
private string m_description;
private bool m_contactAdministrator;
public ReceiverFaultDetail( string message, bool contactAdmin)
: this (message, "" , contactAdmin)
{
}
public ReceiverFaultDetail( string message, string description, bool contactAdmin)
{
this .m_message = message;
this .m_description = description;
this .m_contactAdministrator = contactAdmin;
}
[DataMember(Name = " Message " , IsRequired = true , Order = 0 )]
public string Message
{
get { return m_message; }
set { m_message = value; }
}
[DataMember(Name = " Description " , IsRequired = false , Order = 1 )]
public string Description
{
get { return m_description; }
set { m_description = value; }
}
[DataMember(Name = " ContactAdministrator " , IsRequired = true , Order = 2 )]
public bool ContactAdministrator
{
get { return m_contactAdministrator; }
set { m_contactAdministrator = value; }
}
}
[DataContract]
public class SenderFaultDetail
{
private string m_message;
private string m_description;
private List < string > m_failedBodyElements = new List < string > ();
public SenderFaultDetail( string message, List < string > bodyElements)
: this (message, "" , bodyElements)
{
}
public SenderFaultDetail( string message)
: this (message, "" , null )
{
}
public SenderFaultDetail( string message, string description, List < string > bodyElements)
{
this .m_message = message;
this .m_description = description;
if (bodyElements != null )
this .m_failedBodyElements = bodyElements;
}
[DataMember(Name = " Message " , IsRequired = true , Order = 0 )]
public string Message
{
get { return m_message; }
set { m_message = value; }
}
[DataMember(Name = " Description " , IsRequired = false , Order = 1 )]
public string Description
{
get { return m_description; }
set { m_description = value; }
}
[DataMember(Name = " FailedBodyElements " , IsRequired = true , Order = 2 )]
public List < string > FailedBodyElements
{
get { return m_failedBodyElements; }
set { m_failedBodyElements = value; }
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Configuration;
using System.Collections.ObjectModel;
namespace WcfServiceLibrary1
{
public class ServiceErrorHandlerBehaviorExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof (ServiceErrorHandler); }
}
protected override object CreateBehavior()
{
return new ServiceErrorHandler();
}
}
public class ServiceErrorHandler : ServiceErrorHandlerBehaviorExtensionElement, IErrorHandler, IServiceBehavior
{
public bool HandleError(Exception error)
{
Console.WriteLine( " HandleError " );
return true ;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (fault == null )
{
if (error is InvalidOperationException)
{
FaultException < ReceiverFaultDetail > fe = new FaultException < ReceiverFaultDetail > ( new ReceiverFaultDetail(error.Message, true ), error.Message, FaultCode.CreateReceiverFaultCode( new FaultCode( " InvalidOperationException " )));
MessageFault mf = fe.CreateMessageFault();
fault = Message.CreateMessage(version, mf, fe.Action);
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection < ServiceEndpoint > endpoints, BindingParameterCollection bindingParameters)
{
return ;
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channDisp in serviceHostBase.ChannelDispatchers)
{
channDisp.ErrorHandlers.Add( this );
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (var svcEndpoint in serviceDescription.Endpoints)
{
if (svcEndpoint.Contract.Name != " IMetadataExchange " )
{
foreach (var opDesc in svcEndpoint.Contract.Operations)
{
if (opDesc.Faults.Count == 0 )
{
string msg =
string .Format( " ServiceErrorHandlerBehavior requires a FaultContract(typeof(ApplicationFault)) on each operation contract. The {0} contains no FaultContracts. " , opDesc.Name);
throw new InvalidOperationException(msg);
}
var fcExists = from fc in opDesc.Faults
where fc.DetailType == typeof (ReceiverFaultDetail)
select fc;
if (fcExists.Count() == 0 )
{
string msg = string .Format( " ServiceErrorHandlerBehavior requires a FaultContract(typeof(ApplicationFault)) on each operation contract. " );
throw new InvalidOperationException(msg);
}
}
}
}
}
}
}
只完成上边的代码还不够,由于使用了ExtensionElement所以Config文件里也要相应的配置
< configuration >
< system.web >
< compilation debug ="true" />
</ system.web >
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
< system.serviceModel >
<extensions>
<behaviorExtensions>
<add name="ServiceErrorHandler" type="WcfServiceLibrary1.ServiceErrorHandlerBehaviorExtensionElement, WcfServiceLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
< services >
< service behaviorConfiguration ="WcfServiceLibrary1.Service1Behavior"
name ="WcfServiceLibrary1.MyService" >
< endpoint address ="" binding ="wsHttpBinding" contract ="WcfServiceLibrary1.IMyService" >
< identity >
< dns value ="localhost" />
</ identity >
</ endpoint >
< endpoint address ="mex" binding ="mexHttpBinding" contract ="IMetadataExchange" />
< host >
< baseAddresses >
< add baseAddress ="http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/" />
</ baseAddresses >
</ host >
</ service >
</ services >
< behaviors >
< serviceBehaviors >
< behavior name ="WcfServiceLibrary1.Service1Behavior" >
<!-- To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint above before deployment -->
< serviceMetadata httpGetEnabled ="True" />
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
< serviceDebug includeExceptionDetailInFaults ="False" />
<ServiceErrorHandler/>
</ behavior >
</ serviceBehaviors >
</ behaviors >
</ system.serviceModel >
</ configuration >
客户端代码:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ConsoleApplication1.ServiceReference1;
using System.ServiceModel;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
try
{
ServiceReference1.MyServiceClient proxy = new ConsoleApplication1.ServiceReference1.MyServiceClient();
proxy.GetData(0);
}
catch (FaultException < ReceiverFaultDetail > ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
调试程序可以看出,函数运行的顺序为:
客户端调用服务方法发生异常后,现执行ProvideFault方法,然后执行HandleError方法,客户端收到exception执行Catch。
总结下:
错误处理策略(1)
• 简单的实现
–从业务逻辑/数据访问层抛出CLR异常
– 在服务层捕捉到异常并且转化为适当的Fault
–使用handler记录未捕捉的异常
错误处理策略(2)
• 带有CLR异常的实现
–从业务逻辑/数据访问层抛出CLR异常
– 将CLR异常声明为Fault
–创建错误处理器(error handler)自动将已知的异
常转换为Fault
–记录未捕捉的异常
错误处理策略(3)
• 可互操作的方法
– 从业务逻辑/数据访问层抛出自定义CLR异常
– 将异常定义为数据契约
– 声明自定义异常类型为Fault
– 创建错误处理器(error handler)将自定义异常转换为Fault
– 记录非自定义异常
• 注意:
– 需要对错误处理策略进行良好的定义
• 中立性