Raising and Handling WCF Exceptions

08 August 2011

There are a couple of ways of raising exceptions in a WCF service:

  1. Raise a general exception (the SOAP equivalent of System.Exception) of type System.ServiceModel.FaultException. The WCF runtime will automatically wrap the exception as a SOAP error message for the client
  2. Raise strongly-typed SOAP exceptions that are defined in the service contract using the [FaultContract] attribute

In the following two examples we'll cover both the above approaches.

Raising a FaultException

At its simplest, your service can throw a System.ServiceModel.FaultException which WCF will bundle up as a SOAP error, like so:

  public class WCFExceptionService : IWCFExceptionService
  {
      public string Hello(string name)
      {
          if(string.IsNullOrEmpty(name))
              throw new FaultException("FaultException: name parameter cannot be null");
  
          return "Hello '" + name + "' from WCFExceptionService";
      }
  }

If you need details on creating a simple console-based, self-hosted WCF Service then refer to my earlier post on the subject.

A .NET client can handle the exception as follows:

  public partial class MainWindow : Window
  {
      ServiceReference.WCFExceptionServiceClient m_serviceProxy;

      public MainWindow()
      {
          InitializeComponent();
          m_serviceProxy = new ServiceReference.WCFExceptionServiceClient();
      }

      private void buttonCallWcfService_Click(object sender, RoutedEventArgs e)
      {
          try
          {
              // Example assumes a WPF client with a listbox, textbox and button
              listBox1.Items.Add(m_serviceProxy.Hello(textBox1.Text));
          }
          catch (FaultException ex)
          {
              listBox1.Items.Add(ex.Message);
          }
      }
      :
      :

The main downside to this approach is that, although FaultException has a number of overloaded constructors, the amount of information encapsulated is limited and open to interpretation by the client. More importantly, it is difficult to write code client code that anticipates (and provides specific handlers for) the different types of exceptions that may be thrown by a service.

Raising Strongly-Typed Exceptions

A better approach is to create strongly-typed SOAP exceptions and add them to the service contract. That way, client developers can see and plan up-front for the sort of exceptions they need to deal with.

The following example shows how to:

  • Add a class called NameCannotBeNullFault that will encapsulate our custom SOAP exception to the service's contact
  • Add a [FaultContract] attribute to an operation that can raise the custom exception
  • Throw our custom SOAP exception

Here's the interface definition:

  [ServiceContract]
  public interface IWCFExceptionService
  {
      [FaultContract(typeof(NameCannotBeNullFault))]
      [OperationContract]
      string Hello(string name);
  }

  [DataContract]
  public class NameCannotBeNullFault
  {
      [DataMember]
      public string Message { get; set; }

      [DataMember]
      public string ExampleUsage { get; set; }

  }

And here's the service's implementation:

  public string Hello(string name)
  {
      if (string.IsNullOrEmpty(name))
      {
          NameCannotBeNullFault fault = new NameCannotBeNullFault();
          fault.Message = "Name cannot be null";
          fault.ExampleUsage = "proxy.Hello(\"Your Name Goes Here\");";

          throw new FaultException<NameCannotBeNullFault>(fault);
      }

      return "Hello '" + name + "' from WCFExceptionService";
  }

We can then modify the client to catch the NameCannotBeNullFault exception as shown below. Note that our custom exception is available via the Detail member of FaultException:

  :
  try
  {
    :
  }
  catch (FaultException<ServiceReference.NameCannotBeNullFault> fault)
  {
      listBox1.Items.Add("Message: " + fault.Detail.Message);
      listBox1.Items.Add("Usage: " + fault.Detail.ExampleUsage);
  }
  :