Fault Contract in WCF

Introduction

Any service operation can at any moment encounter an unexpected error. The question is how (if at all) that error should be reported back to the client. Concepts such as exceptions and exception handling are technology-specific and should not transcend the service boundary. In addition, typically error handling is a local implementation detail that should not affect the client, but most often, in a well-designed application, the service is encapsulated and the client lacks anything meaningful to do about the error anyway. A well-designed service should be as autonomous as possible, and should not depend on its client to handle and recover the error. Anything beyond a blank error notification should in fact be part of the contractual interaction between the client and the service. This article describes just how the service and the client should handle these declared faults, and how you can extend and improve on the basic mechanism.

The client can actually encounter three types of errors when attempting to invoke the service. The first type of error is communication errors such as network availability, wrong address, host process not running, and so on. These are manifested on the client-side by the CommunicationException.

The second type of error the client might encounter is related to the state of the proxy and the channels, such as trying to access an already closed proxy, resulting in an ObjectDisposedException, or a mismatch in the contract and the binding security protection level.

The third type of error is the one that originated in the service call, either by the service throwing an exception or as a result of the service calling another object or resource and having that internal call throw an exception.

Example

The following  is an eample of defining the fault contract:

[ServiceContract]
public interface IFaultContract
{
     [OperationContract]
     [
FaultContract(typeof(OrderFault))]
    
string BookOrder(string ISBN, int Quantity);
}
[DataContract]
public class OrderFault
{
     private string info;      
    
public OrderFault(string Message)
     {
         this.info = Message;
     }
     [DataMember]
    
public string msg
     {
         get { return this.info; }
        
set { this.info = value; }
     }
}

The FaultContract attribute is configured to allow multiple usages so that you can list multiple fault contracts in a single operation as in the following:

[ServiceContract]
public interface IFaultContract
{
     [
OperationContract]
     [
FaultContract(typeof(OrderFault))]
    
string BookOrder(string ISBN, int Quantity);
}

Raise the exception from service operation with fault contract.

public
class FaultContractService : IFaultContract
{
     #region IFaultContract Members
    
public string BookOrder(string ISBN, int Quantity)
     {
         int BooksOnHand = 10;
        
//check book quantity vs. order quantity
         if (Quantity <= BooksOnHand)
         {
             return "Order placed";
         }
         else
         {
             throw new FaultException<OrderFault>(new OrderFault(" You ordered     too many books"));
         }
     }
     #endregion
}

Call the serice by proxy

static void Main(string[] args)
{
     FaultContractClient faultContractClient = null;
    
try
     {
           faultContractClient = new FaultContractClient();
          
string orderStatus = faultContractClient.BookOrder("12345", 12);
          
Console.WriteLine("Your OrderStatus is :" + orderStatus);
     }
     catch (FaultException<OrderFault> or)
     {
           Console.WriteLine("Fault Error Message is :" + or.Detail.msg);
     }
     catch (Exception ex)
     {
           Console.WriteLine("Error Message is :" + ex.Message);
     }
     finally
     {
          if (faultContractClient != null)
          {
                faultContractClient.Close();
          }
     }
     Console.ReadLine();
}

You cannot provide a fault contract on a one-way operation, because in theory nothing should be returned from a one-way operation as in the following:

//Invalid definition
[ServiceContract]
interface IMyContract
{
    [
OperationContract(IsOneWay = true)]
    [
FaultContract(...)]
   
void MyMethod( );
}

This will result in an InvalidOperationException at the service load time. Never use a proxy instance after an exception, even if you catch that exception. Do not reuse the callback channel after an exception even if you catch that exception, since the channel may be faulted. Use the FaultContractAttribute with exception classes, as opposed to mere serializable types:

//Avoid:
[OperationContract]
[
FaultContract(typeof(string))]
string BookOrder(string ISBN, int Quantity);
 
//Correct:
[OperationContract]
[
FaultContract(typeof(OrderFault))]
string BookOrder(string ISBN, int Quantity);

Avoid lengthy processing such as logging in IErrorHandler.ProvideFault( ).

With both service classes and callback classes, set IncludeExceptionDetailInFaults to true in debug sessions, either in the config file or programmatically:

public class DebugHelper
{
    public const bool IncludeExceptionDetailInFaults =
   
#if DEBUG
       
true;
    #else    
      
 false;
    #endif
}
[ServiceBehavior(IncludeExceptionDetailInFaults =    
          
DebugHelper.IncludeExceptionDetailInFaults)]
class MyService : IMyContract
{...}

In release builds, do not return unknown exceptions as faults except in diagnostic scenarios.

Consider using the ErrorHandlerBehaviorAttribute on the service for both promoting exceptions to fault contracts and automatic error logging:

[ErrorHandlerBehavior]
class MyService : IMyContract
{....}

Consider using the CallbackErrorHandlerBehaviorAttribute on the callback client for both promoting exceptions to fault contracts and automatic error logging:

[CallbackErrorHandlerBehavior(typeof(MyClient))]
public partial class MyClient : IMyContractCallback
{
    public void OnCallabck()
{.....}
}

That's all about Fault Contract, Thanks for reading.


Similar Articles