Designing New Faults

The last piece of this eleven part series on fault messages
covers advice for channel authors that need to define their own set of faults. Everything
here assumes that you're writing a protocol channel, that you have interesting
failure cases that need to be acted on programmatically, and that your protocol
does not overlap an existing protocol, such as security, reliable messaging, or
transactions. By now, you should either be familiar with all of the classes involved
with faults or going back to read the previous articles in the series.

  1. Basics
    of Failure
  2. Creating
    Faults, Part 1
  3. Creating
    Faults, Part 2
  4. Creating
    Faults, Part 3
  5. The
    Most Distinguished Fault
  6. A
    Historical, Awkwardly Named Fault
  7. Consuming
    Faults, Part 1
  8. Consuming
    Faults, Part 2
  9. Zen
    Faults
  10. Faults
    and HTTP

Let's start with the basic definition of a SOAP fault. It's
mandatory to have a fault action, fault code, and fault reason. The fault
detail is optional.

The fault action is the first round of filtering performed
on faults and so you should define an action that is unique to your protocol. Every
fault that you create for the protocol should have this same action. Following
this rule helps you and everyone else quickly sort out the faults that you need
to handle and let everything else go up to the next protocol layer.

The fault code should be unique to each expected type of
recovery action. For instance, if you have two faults that you need to programmatically
handle in a different fashion, then those faults should have different fault
codes. If you have two faults that are semantically the same, then those faults
should have the same fault code but different fault reasons.

Every fault should have its own descriptive fault reason
that explains why the fault occurred and what the user should do. Fault reasons
are localizable if you're translating your application into multiple languages.

The final piece is the fault detail, which you should only
provide if you have some extra information that hasn't been covered by one of
the previous parts.

Your channel should throw exceptions when an error occurs,
following the standard rules for exceptions in a CommunicationObject, such as using
subtypes of CommunicationException or TimeoutException. The granularity of new
exception subtypes should be similar to the granularity of new fault codes. Your
channel then needs to override GetProperty<FaultConverter> to provide a
converter that translates back and forth between exceptions and fault messages.
Your converter should delegate to the default system FaultConverter if it is unable
to handle the fault or exception.

Following this process, you should not be using
FaultException to wrap faults. Intermediate exception handlers can process your
custom exception classes but don't know enough to be able to pull information
out of a wrapped fault. You should also find by doing this that there are only limited
uses for providing a fault detail.

Next time: Hosting on Machines with Multiple Addresses