WCF开发之异常与错误处理
对于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
– 记录非自定义异常
• 注意:
– 需要对错误处理策略进行良好的定义
• 中立性