WCF开发之异常与错误处理

对于WCF中的异常与错误处理我们必须先了解一个概念SOAP Faults,它实际上就是在服务端和客户端之间来传递错误信息的一种载体。

• 公共语言运行时(CLR)异常无法跨越服务边界
– 未捕捉异常最多到达服务通道(service channel)
– 在报告给客户端之前必须要进行序列化
• 所有的异常都被序列化为SOAP faults
– 基于标准的,可互操作的
– 给予SOAP 1.1 或者 SOAP 1.2 格式

SOAP1.1

ExpandedBlockStart.gif 1.1代码
< s:Envelope
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

ExpandedBlockStart.gif 代码
< s:Envelope  xmlns:s ="http://www.w3.org/2003/05/soap-envelope"
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>区域中配置服务的行为:

ExpandedBlockStart.gif 代码
< system.serviceModel >
  
< 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处理,处于挂起状态的话,此时客户端会处于阻塞状态。

 

ExpandedBlockStart.gif IMyServiceA代码
using  System;
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();
    }
}

 

 

ExpandedBlockStart.gif MyService代码
using  System;
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 " );
        }
    }
}

 

ExpandedBlockStart.gif Config代码
<? xml version="1.0" encoding="utf-8"  ?>
< 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 >

 

 

ExpandedBlockStart.gif ClientTest代码
using  System;
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

FaultException
• 用于抛出简单的错误
• 可以提供错误原因与代码
• 能够提供额外的SOAP错误元素
ExpandedBlockStart.gif 代码
throw   new  FaultException( " An invalid operation has occurred. " );
throw   new  FaultException( new  FaultReason( " An invalid operation has occurred. " ));
throw   new  FaultException( new  FaultReason( " An invalid operation has occurred. " ), FaultCode.CreateSenderFaultCode( null ));
FaultException<T>
• 为SOAP fault提供一个强类型的<T>的简单方法
– T 必须是数据契约或者可序列化类型
• 也能够使用CLR异常类型
– 不利于互操作
throw   new  FaultException < InvalidOperationException > ( new  InvalidOperationException( " An invalid operation has occured. " ),  " Invalid operation. " ,FaultCode.CreateSenderFaultCode( null ));

 

• 为服务错误创建数据契约
–更好地用于互操作

ExpandedBlockStart.gif 代码
[ServiceContract(Name  =   " PhotoUploadContract " , Namespace  =
" http://www.thatindigogirl.com/samples/2006/06 " )]
public   interface  IPhotoUpload
{
    [OperationContract]
    [FaultContract(
typeof (ReceiverFault))]
    [FaultContract(
typeof (SenderFault))]
    
void  UploadPhoto(PhotoLink fileInfo,  byte [] fileData);
}
ExpandedBlockStart.gif 代码
[DataContract(Namespace  =
" 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; }
    }
}
MessageFault
• SOAP Fault的CLR表示
–为了更好地控制错误元素
ExpandedBlockStart.gif 代码
MessageFault mfault  =  MessageFault.CreateFault(
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:

 

ExpandedBlockStart.gif 代码
using  System;
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();

    }
}

 

 

ExpandedBlockStart.gif 代码
using  System;
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
    }
}

 

ExpandedBlockStart.gif 代码
<? xml version="1.0" encoding="utf-8"  ?>
< 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代码:

ExpandedBlockStart.gif 代码
using  System;
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:

 

ExpandedBlockStart.gif IMyService 代码
using  System;
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);
    }
}

 

 

ExpandedBlockStart.gif MyService 代码
using  System;
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 " );
            }
        }
    }
}

 

 

ExpandedBlockStart.gif Fault.cs 代码
using  System;
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; }
        }
    }
}

 

 

ExpandedBlockStart.gif ServiceErrorHandler 代码
using  System;
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文件里也要相应的配置

ExpandedBlockStart.gif App.Config 代码
<? xml version="1.0" encoding="utf-8"  ?>
< 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 >

 

客户端代码:

 

ExpandedBlockStart.gif Client 代码
using System;
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
– 记录非自定义异常
• 注意:
– 需要对错误处理策略进行良好的定义
• 中立性

 

转载于:https://www.cnblogs.com/CharlesLiu/archive/2010/03/09/1667041.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值