I promised to start going over the object model for faults today, and I've split that coverage into two parts. The first part highlights the MessageFault class and creating fault messages (yes, it's a bit confusing that those are separate things). The second part goes over the FaultCode and FaultReason classes. I'm actually leaving quite a bit of detail out because there are a lot of moving parts involved in fault messages, many of which won't make sense until we get farther in.

A fault message consists of a standard message whose body is represented by the MessageFault class. There are several static overloads on the base Message class that are used to create fault messages. The overloads give you various combinations of the fault code, fault reason, and fault detail that I talked about last time. No matter which option you choose, all of these code paths eventually end up creating a message with an XML body writer that is generated from a MessageFault.

public static Message CreateMessage(MessageVersion version, MessageFault fault, string action);
public static Message CreateMessage(MessageVersion version, FaultCode faultCode, string reason, string action);
public static Message CreateMessage(MessageVersion version, FaultCode faultCode, string reason, object detail, string action);

The Message is just a carrier, which means that everything interesting is on the MessageFault class.

public abstract class MessageFault
{
protected MessageFault();

public virtual string Actor { get; }
public abstract FaultCode Code { get; }
public abstract bool HasDetail { get; }
public bool IsMustUnderstandFault { get; }
public virtual string Node { get; }
public abstract FaultReason Reason { get; }

public static MessageFault CreateFault(FaultCode code, FaultReason reason);
public static MessageFault CreateFault(FaultCode code, string reason);
public static MessageFault CreateFault(Message message, int maxBufferSize);
public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail);
public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer);
public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer, string actor);
public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer, string actor, string node);
public T GetDetail<T>();
public T GetDetail<T>(XmlObjectSerializer serializer);
public XmlDictionaryReader GetReaderAtDetailContents();
protected virtual XmlDictionaryReader OnGetReaderAtDetailContents();
protected virtual void OnWriteDetail(XmlDictionaryWriter writer, EnvelopeVersion version);
protected abstract void OnWriteDetailContents(XmlDictionaryWriter writer);
protected virtual void OnWriteStartDetail(XmlDictionaryWriter writer, EnvelopeVersion version);
public static bool WasHeaderNotUnderstood(MessageHeaders headers, string name, string ns);
public void WriteTo(XmlDictionaryWriter writer, EnvelopeVersion version);
public void WriteTo(XmlWriter writer, EnvelopeVersion version);
}

You should be able to immediately pick out how to access the fault code, fault reason, and fault detail given what's been covered so far. The fault detail is available as either a strongly typed object or the underlying XML stream. Accessing the detail as a strongly typed object is just a convenience. Under the covers, everything is going through the XML reader. The only thing that you're saving by using typed objects is potentially typing lines of code.

The serializer that keeps popping up in the methods here is the one that gets applied to the detail object. We do have a default serializer that is used if you do not supply your own. The only difference between the default serializer for a fault detail and the default serializer for other messages is that in the fault detail case, the MaxItemsInObjectGraph setting is cranked way up.

Next time: Creating Faults, Part 2