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