Want to follow this site? Here's the RSS feed.

Understanding WCF Faults in Silverlight

July 08, 2009

If you like this document, please consider writing a recommendation for me on my LinkedIn account.

Contents

Introduction

Regardless of what platform you are working with, you need some mechanism for dealing with errors.  When it comes to using WCF in Silverlight we are all very fortunate to be able to build out solutions on the back of many tried and true techniques for error management. When working in this context, we are dealing with basic SOAP messages (that is, XML) and WCF's abstraction of those messages, neither of which are new to the world.

If you haven't done so already, you should read the first part of this document entitled Understanding WCF Services in Silverlight (here after "previous document").  You may consider this document an appendix to that one.  That document explains WCF and its integration into the world of Silverlight from a very low level. This document extends that one to explain error management in the world of Silverlight 3. Understanding of the previous document is a prerequisite for understanding this one.

SOAP Review

Before we take a look at error management over SOAP services, we will take a moment to review SOAP messaging. SOAP messaging is based on the concept of sending SOAP messages back and forth between client and service. A SOAP message is the package by which a client and a service communicate. Web services (a.k.a. SOAP services) are not "connected" like a chat channel. Instead, they are "disconnected" like an e-mail system. The client sends the service a message and the service, optionally, sends the client message back (depending on if the client requested a message; one way messaging is very common). Below is a sample message sent from a client to the service:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <GetPersonData xmlns="http://www.netfxharmonics.com/service/Contact/2009/07/">
      <personGuid>F488D20B-FC27-4631-9FB9-83AF616AB5A7</personGuid>
    </GetPersonData>
  </s:Body>
</s:Envelope>

Essentially, this message is calling the "GetPersonData" operation on the service, sending "personGuid" as a parameter. This is stored in the message body (which is distinct from, say, a message header, which is not present in this example). The body is then stored in an envelope. When this message is sent via an HTTP POST to, for example, /Person.svc with the SOAP-Action HTTP header set to the name of the SOAP operation, WCF calls the appropriate operation (set by the SOAP-Action HTTP header).  For more information on the mechanics of WCF and how to work with WCF directly see my XmlHttp Service Interop series at http://www.netfxharmonics.com/2007/05/XmlHttp-Service-Interop-Part-3-XML-Serialization).

Here is a message the service could possibly send back to the client:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <GetPersonDataResponse xmlns="http://www.netfxharmonics.com/service/Contact/2008/11/">
      <GetPersonDataResult xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <City>Unknown</City>
        <FaultDetail i:nil="true" xmlns:a="http://schemas.datacontract.org/2004/07/General.Service"/>
        <FirstName>John</FirstName>
        <Guid>89DEA4C5-84A0-45db-A60D-CE49F214EA50</Guid>
        <LastName>Doe</LastName>
        <PostalCode>66062</PostalCode>
        <State>KS</State>
      </GetPersonDataResult>
    </GetPersonDataResponse>
  </s:Body>
</s:Envelope>

Here we have the data for "GetPersonData" in a result object, wrapped in a response container. This is then, once again, wrapped in a standard SOAP body, inside a standard SOAP envelope. Pretty simple stuff. Someone sends a message, the receiver sends something back. The question now becomes... what happens when something goes wrong?

Handling Errors

When doing error management with SOAP-based WCF, you are doing just that: error management. You are NOT doing exception management. Exceptions don't exist over the wire. How can an exception from WCF being handled by a PHP client? How can System.InvalidOperationException be handled by a Java client? These scenarios make no sense. Therefore, in the world of SOAP services, you have no exceptions. Instead, you have a concept called a "fault".  A fault is a SOAP error.  WCF, Java, and PHP can all deal with SOAP (since SOAP is just XML), thus, they each can deal with faults with no problem. Given this, we may adjust our terminology at this point from "error management" to "fault management".

In reality, a fault is nothing more than a piece of XML formatted in a specific way. One side sends it, the other side receives it. It's a really simple setup. (As a side note, please keep in mind that this is not a document on the internal mechanics of SOAP. That's a discussion for a different document. Fortunately, though, the understanding of faults in SOAP doesn't require deep SOAP knowledge.)

When an exception is thrown in a SOAP-based WCF service, typically a fault is sent over the wire. A SOAP-based client (be it WCF, WCF in Silverlight, PHP, or Java), then obtains it, parses it and handles it accordingly. So what's a SOAP fault look like? (you'll see later that this is an example of a very poor practice, but it's a good fault example.)

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <s:Fault>
      <faultcode>s:Client</faultcode>
      <faultstring xml:lang="en-US">Really descriptive message here.</faultstring>
      <detail>
        <InvalidOperationException xmlns="http://schemas.datacontract.org/2004/07/System" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema">
          <ClassName i:type="x:string" xmlns="">System.InvalidOperationException</ClassName>
          <Message i:type="x:string" xmlns="">Really descriptive message here.</Message>
          <Data i:nil="true" xmlns=""/>
          <InnerException i:nil="true" xmlns=""/>
          <HelpURL i:nil="true" xmlns=""/>
          <StackTraceString i:nil="true" xmlns=""/>
          <RemoteStackTraceString i:nil="true" xmlns=""/>
          <RemoteStackIndex i:type="x:int" xmlns="">0</RemoteStackIndex>
          <ExceptionMethod i:nil="true" xmlns=""/>
          <HResult i:type="x:int" xmlns="">-2146233079</HResult>
          <Source i:nil="true" xmlns=""/>
        </InvalidOperationException>
      </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

Notice that even this fault is a SOAP message: the contents are in a SOAP body, wrapped in a SOAP envelope. So, all we really did is use a concept we already knew and added something to it. By sending a "Fault" XML element in a message body, you are effectively telling the client that some error occurred on the service-side. This is a well-known pattern that all SOAP services follow.

If you look closely at this SOAP message (which, of course, is also a fault), you will notice that the "Fault" element has three children: "faultcode", "faultstring" and "detail". You may recognize some parts of this specific fault.  That is, you see the name "InvalidOperationException" as well as the "Message", "InnerException", and other elements as well-known properties of exceptions. Keep in mind, though, that none of these have anything to do with faults. These are in the "detail" section of the fault, not in any part that actually matters.  Clear as mud?

Faults don't rely on the detail element. The information in this block is simply for the end developer to obtain custom information. Since WCF doesn't rely on this information, neither should you (i.e. it won’t always be there). A fault is defined by the "faultstring" element, not the "detail" element. The "faultstring" element contains is the actually message that you will want to look at when debugging.

Not even the "faultcode" element isn’t directly used by most people. This is a well-defined enumeration with the possible values of "Client", "Server", "VersionMismatch", and "MustUnderstand" (prefixed with the XML namespace). These help you to get some idea of the category of error that occurred, but, in the majority of cases, you won't be working with it directly.  This is all to reinforce the fact that it’s faultstring that contains the actual error, not faultcode or details.

Applying to WCF

In the world of WCF for .NET, all of this is essentially internal mechanics that most mid-level or entry-level developers will only see on a senior-level developer's whiteboard (i.e. they won't use it directly). This is because WCF knows to look for faults and knows how to handle them: it will take the content from faultstring and set that as the Message in a System.ServiceModel.FaultException object. This instance of a FaultException object is developers directly work with.

Now pay attention very closely: you can't just throw an exception in a WCF service and expect it to show up on your client (or to show up over the wire for that matter!) Why is this? Thikn about it... do you really want an exception which contains the entire stack trace and, therefore, a snapshot of the private internal mechanics of your system thrown to a client computer? Obviously not. Since WCF knows is a horrible security violation to send stack traces over the wire, it's not going to allow this.  In fact, that fault SOAP-message shown early is hopefully a picture from fiction, not reality (you kind of figured there were a lot of unneeded xml elements, didn't you?)

What you actually do, is you throw one of two special exceptions: System.ServiceModel.FaultException or System.ServiceModel.FaultException<T> (which inherits from the first one). When you throw an exception which isn't one of these, like System.InvalidOperationException, you will see the following over the wire (i.e. throw new System.InvalidOperationException("This is my error message.")):

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <s:Fault>
      <faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</faultcode>
      <faultstring xml:lang="en-US">The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the &lt;serviceDebug&gt; configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.</faultstring>
    </s:Fault>
  </s:Body>
</s:Envelope>

You may think "not pretty". Trust me, that IS pretty. Ugly would be showing your internal stack trace over the wire. Thank you WCF for protecting us from ourselves.

Now, if you throw System.ServiceModel.FaultException instead of System.InvalidOperationException (i.e. throw new System.ServiceModel.FaultException("This is my error message."), you will see this:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <s:Fault>
      <faultcode>s:Client</faultcode>
      <faultstring xml:lang="en-US">This is my error message.</faultstring>
    </s:Fault>
  </s:Body>
</s:Envelope>

Very simple and to the point. You told it "This is my error message." and it sent "This is my error message." Can't get much simpler than that.  In fact, the simplicity stays even when you work with the exception on the client side.  Over there, the client will also have a FaultException.  The “faultstring” from the SOAP message will be places into the Message property of the FaultException object on the client.  An example of this will be shown in a bit.

Let's make this a bit more complex (read: more useful/powerful) by changing things up slightly.

As I've said, there are two fault exception types in WCF: one generic, one not. Use of the generic one allows you to send more information to the client by allowing entire objects to be serialized over the wire as part of the fault "detail" element. To see an example of this, take a look at the following custom type:

namespace Contact.Service
{
    [DataContract]
    public class FaultDetail
    {
        //- @ErrorCode -//
        /// <summary>
        /// Custom business-specific error code.
        /// </summary>
        [DataMember]
        public Int32 ErrorCode { get; set; }
        //- @Type -//
        /// <summary>
        /// Specifies the type of error.
        /// </summary>
        [DataMember]
        public String Type { get; set; }
    }
}

Now, instead of throwing the non-generic fault exception, let's throw the generic one, using the above type as the generic parameter. For example:

//- @GetPersonData -//
public Person GetPersonData(String personGuid)
{
    //+ ...validation here...     
    FaultDetail faultDetail = new FaultDetail
    {
        Type = "Validation",
        ErrorCode = 63873928
    };     //+ throw     
    throw new FaultException<FaultDetail>(faultDetail, "Invalid guid.");
    //+ ...more logic here...
}

Given this, we now have this over the wire:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <s:Fault>
      <faultcode>s:Client</faultcode>
      <faultstring xml:lang="en-US">Invalid guid.</faultstring>
      <detail>
        <FaultDetail xmlns="http://schemas.datacontract.org/2004/07/Contact.Service" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
          <ErrorCode>63873928</ErrorCode>
          <Type>Validation</Type>
        </FaultDetail>
      </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

As you can see, using the generic FaultException type, you can send all kinds of stuff over the wire.

Fault Contracts

While this is great for dynamically typed systems (i.e. PHP and JavaScript), we don't yet have a complete solution for strong-typed ones (i.e. .NET). What’s that? Again, think about it: How does the other end know what type is "FaultDetail". Is that ABCCorp.Web.FaultDetail? ABCCorp.Service.FaultDetail? What's the namespace? In what assembly?

To make this happen in .NET, we have to introduce something called a "fault contract". As you recall from the previous document, WCF has service contracts and operation contracts. Fault contracts are just another contract type in the contact family.

These are setup by applying the System.ServiceModel.FaultContractAttribute attribute to various operations of your service contract.  When you apply this attribute, you must tell it what type you would like to allow to be thrown from that specific operation.  This is simply done by giving the constructor of the fault contract attribute to type object of your fault detail type.  For example, below is our operation contract with the fault contract applied.

//- GetPersonData -//
[OperationContract]
[FaultContract(typeof(FaultDetail))]
Person GetPersonData(String personGuid);

If you're following along and paying close attention you should be thinking "How in the WORLD does that help the client? This is on the server! The same SOAP data is going to come over the wire and the client has no more information." Well, as you recall from the previous document, the contracts must be both on the service-side and on the client-side. In the case of .NET, these are the same types since the contracts are in an assembly shared by both client and service (remember, use of the "Add Service Reference" for .NET-to-.NET communication provides ZERO value and only complicates life).

So, if you are doing .NET-to-.NET, your client contract DOES have this and WCF on the client will automatically know to use that specific type when deserializing the “detail” part of the fault SOAP message.  Let's see this in action.

First, let's review our previous setup:

//+ basicHttpBinding
BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
EndpointAddress endpointAddress = new EndpointAddress("http://localhost.:1003/Person.svc");
IPersonService personService = new ChannelFactory<IPersonService>(basicHttpBinding, endpointAddress).CreateChannel();
//+
Person person = personService.GetPersonData("F488D20B-FC27-4631-9FB9-83AF616AB5A6");

This is all that's required to make a .NET-to-.NET WCF call. Setup a address-binding-contract combination to create a channel, then call the operation through the channel. Not a big deal. Now let's add one level of fault handing:

//+ basicHttpBinding
BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
EndpointAddress endpointAddress = new EndpointAddress("http://localhost.:1003/Person.svc");
IPersonService personService = new ChannelFactory<IPersonService>(basicHttpBinding, endpointAddress).CreateChannel();
//+
try
{
    Person person = personService.GetPersonData("F488D20B-FC27-4631-9FB9-83AF616AB5A6");
}
catch (FaultException ex)
{
    //+ without fault contract     
    WriteToOutput("There was an error...(FaultException)");
    //+ this is the message from “faultstring” in the SOAP message     
    WriteToOutput(ex.Message);
}

This example shows handling a fault exception without having the first clue what type is being sent. We can, however, access the fault message by accessing Message property of the fault.  So, in this case, “ex.Message” will contain “Invalid guid.”.  Now let’s go one step further:

BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
EndpointAddress endpointAddress = new EndpointAddress("http://localhost.:1003/Person.svc");
IPersonService personService = new ChannelFactory<IPersonService>(basicHttpBinding, endpointAddress).CreateChannel();
//+
try
{
    Person person = personService.GetPersonData("F488D20B-FC27-4631-9FB9-83AF616AB5A6");
}
catch (FaultException<FaultDetail> ex)
{
    WriteToOutput("There was an error...(FaultException<FaultDetail>");
    //+
    FaultDetail detail = ex.Detail;
    WriteToOutput("ErrorCode: " + detail.ErrorCode.ToString());
    WriteToOutput("Type: " + detail.Type);
    //+ again, this is the message from “faultstring” in the SOAP message
    WriteToOutput(ex.Message);
}
catch (FaultException ex)
{
    WriteToOutput("There was an error...(FaultException)");
    WriteToOutput(ex.Message);
}

In this example, we are taking advantage of the fault contract on the client operation.  Notice that we have access to the  actual detail content that was sent over the wire.  Not only that, but we can access it as a true type via the Detail property of our generic fault exception. This makes things a lot easier.

Remember, the only difference these two examples is the presence of the fault contract on the client operation. If the fault contract were NOT on the client operation, then the "FaultException" catch-block would be caught, not the generic one.

Throwing Exceptions (Bad!)

At this point, you should be able to see that the original fault SOAP message I showed you involving the InvalidOperationException is, in fact, not entirely fictional. It is possible to send an exception over the wire. All you would need to do is something like this (in addition to setting up a fault contract on the client operation):

String message = "Invalid guid.";
throw new FaultException<InvalidOperationException>(new InvalidOperationException(message), message);

Having said this, throwing an exception over the wire is very poor form. It's not as bad as publicly exposing a private field, using the "var" C# keyword for non-anonymous types, or camelCasing() .NET method names, but it's still viewed as a very poor practice. Why? Well, you tell me. Take a look at the following two examples:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <s:Fault>
      <faultcode>s:Client</faultcode>
      <faultstring xml:lang="en-US">Really descriptive message here.</faultstring>
      <detail>
        <InvalidOperationException xmlns="http://schemas.datacontract.org/2004/07/System" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema">
          <ClassName i:type="x:string" xmlns="">System.InvalidOperationException</ClassName>
          <Message i:type="x:string" xmlns="">Really descriptive message here.</Message>
          <Data i:nil="true" xmlns=""/>
          <InnerException i:nil="true" xmlns=""/>
          <HelpURL i:nil="true" xmlns=""/>
          <StackTraceString i:nil="true" xmlns=""/>
          <RemoteStackTraceString i:nil="true" xmlns=""/>
          <RemoteStackIndex i:type="x:int" xmlns="">0</RemoteStackIndex>
          <ExceptionMethod i:nil="true" xmlns=""/>
          <HResult i:type="x:int" xmlns="">-2146233079</HResult>
          <Source i:nil="true" xmlns=""/>
        </InvalidOperationException>
      </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

... and ... 

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <s:Fault>
      <faultcode>s:Client</faultcode>
      <faultstring xml:lang="en-US">Invalid guid.</faultstring>
      <detail>
        <FaultDetail xmlns="http://schemas.datacontract.org/2004/07/Contact.Service" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
          <ErrorCode>63873928</ErrorCode>
          <Type>Validation</Type>
        </FaultDetail>
      </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

Notice that the first example (the exception) has many items going over the wire that are never even used, thus wasting serialization and deserialization processing time as well as bandwidth, not to mention making the SOAP message more difficult to read. Notice also that each of the elements in the second example (the fault detail) relate to the task at hand. Therefore, you should always define a fault detail type when dealing with fault exceptions.

Applying to Silverlight

Everything we discussed so far relating to the service applies to Silverlight. That is, you may use the same service for both Silverlight and .NET.  However, if you would like to use the strongly typed fault details in Silverlight as we discussed in .NET, then you need to apply the fault contract attribute to the Silverlight async operation (note: technically this has nothing to do with Silverlight; if you are using async calls in .NET, then Silverlight and .NET will share those).  More specifically, the "Begin" method of your operation contract. Here's an example:

//- BeginGetPersonData -//
[OperationContract(AsyncPattern = true)]
[FaultContract(typeof(FaultDetail))]
IAsyncResult BeginGetPersonData(String personGuid, AsyncCallback callback, Object state);

Note also that since Silverlight is NOT .NET, you have to get the fault detail from the .NET world over to the Silverlight world.  Now, given that nothing about the type is different in the .NET and Silverlight worlds, you can easily share it between the two.  On way to do this is by using the “Add As Link” feature in Visual Studio.  More more ways of sharing types and assemblies between .NET and Silverlight see my document entitled Reusing .NET Assemblies in Silverlight.

Now given this setup, you can upgrade your service access by wrapping your call to the "End" method in a try/catch block.

BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
EndpointAddress endpointAddress = new EndpointAddress("http://localhost.:1003/Person.svc");
IPersonService personService = new ChannelFactory<IPersonService>(basicHttpBinding, endpointAddress).CreateChannel();
//+
AsyncCallback asyncCallBack = delegate(IAsyncResult result)
{
    try { Person person = ((IPersonService)result.AsyncState).EndGetPersonData(result); Write(person.FirstName); }
    catch (FaultException<FaultDetail>)
    {
        Write("There was an error...(FaultException<FaultDetail)");
    }
    catch (FaultException)
    {
        Write("There was an error...(FaultException)");
    }
    catch (Exception)
    {
        Write("There was an error...(Exception)");
    }
};

When you run this, barring any other problems, you can expect that the FaultException<FaultDetail> catch block be called, since that's what's going on the .NET side.  Everything will work the same from here on out.  In fact, if you are doing async calls in the .NET world, your code won’t have any differences at all.

Service Modification for Silverlight

Notice that when I said that the FaultException<FaultDetail> catch block would be called, I said “barring any other problems”.  This is a very important little phrase.  In fact, if you did actually try to run the above example, you would see something rather shocking: the FaultException<FaultDetail> block was NOT hit.  Instead, the Exception block was.  At this point, if you are confused... then you’re probably following along just fine.

Thus, you begin debugging. First, you check the exception message (ex.Message). It says "The remote server returned an error: NotFound.". What? Whatever. Then you realize that you need to see what WCF sent back. So, you open fiddler and rerun the example. You see the following:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <s:Fault>
      <faultcode>s:Client</faultcode>
      <faultstring xml:lang="en-US">Invalid guid.</faultstring>
      <detail>
        <FaultDetail xmlns="http://schemas.datacontract.org/2004/07/Contact.Service" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
          <ErrorCode>63873928</ErrorCode>
          <Type>Validation</Type>
        </FaultDetail>
      </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

Well, that's correct. WCF is sending the correct data back. At this point, you go to the next step of debugging by proceeding to bang you head against a wall.

What's the problem?

Here it is: Silverlight a browser plugin. Therefore, Silverlight must obey the rules of plugins and are also limited to the information given to them by their host browser. In the case of fault exceptions, faults are send over an HTTP connect with a non-200 HTTP status code. This makes sense since 200 means “OK” and fault means “not-OK”. Browsers do not send this kind of information to plugins. Thus, not Microsoft’s fault (so, stop cursing them—redirect any and all of that to the IE team). Once we see this truth about the internal mechanics, we can see a glimpse of hope.

How so? Well, the problem clearly has nothing to do with faults. It has to do with the HTTP non-200 status code. Therefore, in order for Silverlight to see the fault exception we need to make sure the service with which the Silverlight client is communicating sends a 200 HTTP status code at all times, regardless of a success or not. This is incredibly easy in WCF using a concept known as an ErrorHandler.

ErrorHandlers are classes which implement the System.ServiceModel.Dispatcher.IErrorHandler interface. One of the required methods that this interface requires for you to implement has the following signature:

void ProvideFault(Exception error, MessageVersion version, ref Message fault)

By implementing this method and registering the ErrorHandler to WCF, you can effectively change the HTTP status code. Below is one implementation of this (notice that name is HttpStatusCode200ErrorHandler not SilverlightErrorHandler-- this can be used for anything that doesn't support non-HTTP 200 codes):

#region Copyright
//+ Themelia Pro 2.0 - Core Module
//+ Copyright © Jampad Technology, Inc. 2007-2009
#endregion
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
//+
namespace Themelia.ServiceModel.Behavior
{
    /// <summary>
    /// Sets the HTTP code to 200 for faults.
    /// </summary>
    public class HttpStatusCode200ErrorHandler : IErrorHandler
    {
        //- @ServiceType -//
        public Type ServiceType { get; set; }

        //+
        //- @Ctor -//
        public HttpStatusCode200ErrorHandler(Type serviceType)
        {
            ServiceType = serviceType;
        }

        //+
        //- @HandleError -//
        public bool HandleError(Exception error)
        {
            return false;
        }

        //- @ProvideFault -//
        public virtual void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            fault.Properties[HttpResponseMessageProperty.Name] = new HttpResponseMessageProperty
            {
                StatusCode = System.Net.HttpStatusCode.OK
            };
        }
    }
}

As you can see, on the created fault there is a Properties bag. By adding our own HttpResponseMessageProperty to this bag under the name of HttpResponseMessageProperty.Name, we can effectively change the HTTP status code.

To install an error handler, just create a service behavior.  A service behavior is much like an operation behavior, but, as you may have guessed, is for a service.  These are really just classes that implement the System.ServiceModel.Description.IServiceBehavior interface.  In our case, all we care about is using the ApplyDispatchBehavior method to install our error handler (this should all sound very familiar since we did something very analogous with an operation behavior and invoker earlier.)  Here’s a sample behavior to install the error handler:

#region Copyright
//+ Themelia Pro 2.0 - Core Module
//+ Copyright © Jampad Technology, Inc. 2007-2009
#endregion
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
//+
namespace Themelia.ServiceModel.Behavior
{
    /// <summary>
    /// Applies HttpStatusCode200ErrorHandler.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class HttpStatusCode200Behavior : Attribute, IServiceBehavior
    {
        //- @AddBindingParameters -//
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
            //+ blank
        }

        //- @ApplyDispatchBehavior -//
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
            {
                dispatcher.ErrorHandlers.Add(new HttpStatusCode200ErrorHandler(serviceDescription.ServiceType));
            }
        }

        //- @Validate -//
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            //+ blank
        }
    }
}

Now we just need to install the behavior. You can do this in a number of different ways, but I almost always use the service host factory approach.

The purpose behind one of these is to create a service host, which in turn hosts... (drum roll, please)... a service.  As with most WCF features, these are very powerful.  We use them to do all kinds of programmatic service alterations.  For example, we can add custom endpoints, setup metadata, apply more security, or, in our case, install a service behavior (which, in turn, installs our error handler).

A service host factory is simply a class which inherits from System.ServiceModel.Activation.ServiceHostFactory.  To create our service host factory , we are going override the following signature of a newly created service host factory:

ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)

In this method, we are going to create our service host, add our behavior to that host, then return it. Here's the end result:

using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
//+
namespace Contact.Service.Activation
{
    public class PersonServiceHostFactory : WebServiceHostFactory
    {
        //- @CreateServiceHost -//
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            ServiceHost host = new ServiceHost(serviceType, baseAddresses);
            //+ HTTP 200
            host.Description.Behaviors.Add(new Themelia.ServiceModel.Behavior.HttpStatusCode200Behavior());
            //+
            return host;
        }
    }
}

Now, we just need our service host to use this factory. Your service host in the context of the web is the /SOMETHING.svc file. All we need to do is open this file and set the service directive "Factory" property to the name of our factory. For example:

<%@ ServiceHost Service="Contact.Service.PersonService" Factory="Contact.Service.Activation.PersonServiceHostFactory" %>

Now we are done.  When your access this service, the service host factory will create the host and add the error handler.  Then, when any error occurs,t he error handler will catch the error and set the HTTP status code to 200.  Thus, the browser will send the fault message to Silverlight and we’ll finally see that the correct FaultException<FaultDetail> catch block is hit.

Conclusion

In sum, WCF provides a very straight-forward, easy to implement fault management solution, which retains the idioms of C# and the purity of the SOAP standard. Not only that, but with a simple flip of a switch Silverlight works flawlessly with the feature that WCF has had since its first release.

Faults are really nothing more than SOAP messages of a certain format.  If there is an error, WCF is smart enough to not just serialize some exception, but, instead, send a fault SOAP message over the wire.  Thus, making the entire error handling process end-to-end very streamlined.

Whereas Silverlight 2 requires some extra massaging in order to get fault contracts implemented, Silverlight 3 has the feature built right in. Using the techniques described in this document, Silverlight 3 developers should be able to keep with the streamlined nature of WCF without the need for any custom fault management.

Samples for this document may be found here.

Creative Commons License
This work is licensed under a Creative Commons Attribution 2.5 License.

Mini-icons are part of the Silk Icons set of icons at famfamfam.com