Exception Management

patterns & practices Developer Center

  • How do I implement a global exception handler?
  • What is a fault contract?
  • How do I define a fault contract?
  • How do I avoid sending exception details to the client?

How do I implement a global exception handler?

To implement a global exception handler, subscribe to the Faulted event of a service host object. When you receive the faulted event, you can determine the cause of a failure and perform the necessary actions to abort or restart the service.

The following code snippet shows how to subscribe to the Faulted event:

// hosting a WCF service 
ServiceHost customerServiceHost;
customerServiceHost = new ServiceHost(…);
…
// Subscribe to the Faulted event of the customerServiceHost object
customerServiceHost.Faulted += new EventHandler(faultHandler);
…
// FaultHandler method - invoked when customerServiceHost enters the Faulted state
void faultHandler(object sender, EventArgs e)
{     // log the reasons for the fault  ..
}

Additional Resources

What is a fault contract?

A fault contract is a message contract that details the set of exceptions that may be reported to the caller. You can specify the possible faults that can occur in your WCF service. This prevents exposing exception details beyond the defined set to your clients. Because a fault contract lists the types of errors that a WCF service can throw, it also allows your clients to distinguish between contracted faults and other possible errors.

A fault contract can be set on an operation in your service as follows:

[ServiceContract]
interface ICalculator
{
   [OperationContract]
   [FaultContract(typeof(DivideByZeroException))]
   double Divide(double number1,double number2);
}

Additional Resources

How do I define a fault contract?

To define a fault contract, apply the FaultContract attribute directly on a contract operation, specifying the error detailing type as shown below:

[ServiceContract]
interface ICalculator
{

   [OperationContract]
[FaultContract(typeof(DivideByZeroException))]
   double Divide(double number1,double number2);

}

In this example, the FaultContract attribute is limited to the Divide method. This means only that method can throw that fault and have it propagated to the client. Also, the service must throw exactly the same detailing type listed in the fault contract to propagate the exception, as shown below:

class MyService : ICalculator
{
   public double Divide(double number1,double number2)
   {

      throw new FaultException<DivideByZeroException>(new DivideByZeroException());
   }
}

Additional Resources

How do I avoid sending exception details to the client?

To avoid sending potentially sensitive exception details to the client, use the FaultContract attribute in a service contract to specify the possible faults that can occur in your WCF service.

The following code snippet shows how to use the FaultContract attribute to return error information:

  1. Define the type to pass the details of SOAP faults as exceptions from a service back to a client.

    [DataContract]
    public class DatabaseFault
    {
        [DataMember]
        public string DbOperation;
        [DataMember]
        public string DbReason
        [DataMember]
        public string DbMessage;
    }
    
  2. Use the FaultContract attribute in the ListCustomers method to generate SOAP faults.

    [ServiceContract]
    public interface ICustomerService
    {
        // Get the list of customers
        [FaultContract(typeof(DatabaseFault))]
        [OperationContract]
        List<string> ListCustomers();
        …
    }
    
  3. Create and populate the DatabaseFault object with the details of the exception in the Service implementation class, and then throw a FaultException object with the DatabaseFault object details.

    catch(Exception e)
    {    DatabaseFault df = new DatabaseFault();
        df.DbOperation = "ExecuteReader";
        df.DbReason = "Exception in querying the Northwind database.";
        df.DbMessage = e.Message;
        throw new FaultException<DatabaseFault>(df);
    }
    

Additional Resources