Introduction
In this tip, I want to demonstrate how you add exception handling to your Windows Communication Foundation (WCF) applications. Exception handling in WCF applications is slightly different from exception handling in other .NET applications. The reason for this is that a WCF service may need to serve a client that was implemented using a technology other than the .NET Framework. For this reason, a slightly different approach is required when implementing exception handling in your WCF applications.
Background
This tip will assume that the reader is familiar with implementing WCF services and clients. It is not an introductory WCF article.
A WCF service exposes certain services (or functionality) to client applications. For example, a financial WCF service may expose services relating to VAT calculations or calculating mortgage repayments. The WCF client connects to the WCF service and invokes the exposed services as required. All well and good. However, what happens in the event of an exception being thrown by the WCF service? This tip will address this question from both the client and the service side of a WCF application.
- How should the WCF service throw an exception to a WCF client?
- How should a WCF client catch an exception thrown by a WCF service?
As mentioned earlier, a WCF service exposes certain services (or functionality) to be consumed by client applications. These client applications may not necessarily be .NET applications. Therefore the WCF service should NOT throw a .NET exception to the client as it wouldn't be understood by the client WCF application unless it was a .NET application.
What the WCF service needs to do instead is to throw an exception that is language agnostic and that can be understood by any client written in any language, including clients that are NOT implemented using the .NET Framework. Therefore a Java client should be able to consume a WCF service and understand the exception information that is returned should the WCF service throw an exception.
So how does a WCF service throw a client agnostic exception?
Throwing WCF Service Exceptions
The FaultException
is a .NET class that is designed to be used with WCF applications. They are strongly typed SOAP faults that can be thrown to any client irrespective of its underlying technology.
In the example code below, the WCF service implements a try / catch block. The catch block throws an exception of type FaultException
.
try
{
}
catch (Exception ex)
{
throw new FaultException(ex.Message);
}
The FaultException
class can also be initialized using the FaultCode
and FaultReason
classes to add more meaningful information to your WCF service exception.
The following example code adds a FaultCode
to the exception.
try
{
}
catch (Exception ex)
{
throw new FaultException(ex.Message, new FaultCode("Add SOAP fault code here"));
}
The following example code adds both a FaultCode
and a FaultReason
to the exception.
try
{
}
catch (Exception ex)
{
throw new FaultException(new FaultReason("Add SOAP fault reason here"), new FaultCode("Add SOAP fault code here"), ex.Message);
}
So it is entirely possible to return meaningful information to a WCF client using the aforementioned classes. It is also possible to specify a detail object that can contain more specific or customized information you want to return to the WCF client.
Adding Specific Information to Your WCF Exception
You can instantiate the FaultException
class with an instance of a <TDetail>
class. This <TDetail>
class may contain more detailed information specific to the exception, or more verbose information such as information from the Exception
such as the InnerException
. For debugging purposes, you could add TargetSite
, Source
and StackTrace
.
Example usage of instantiating the FaultException
class with a serializable <TDetail>
object.
try
{
}
catch (Exception ex)
{
throw new FaultException<TDetail>(TDetail);
}
Here is an example of how you can implement a <TDetail>
object and use it to return more detailed exception information to the WCF client.
Implement a <TDetail> Class
For the purposes of this tip, I will implement a <TDetail>
class that adds Exception
information which is useful for debugging / diagnostics. However, your own <TDetail>
class can contain whatever information is appropriate to your application or specific to the service that is throwing it. For example, if the service is returning a Customer
object, then you may want to return a <TDetail>
object that exposes the Customer ID (to allow the client to display a specific error message).
Firstly, we need to define our <TDetail>
class. The class is called UnexpectedServiceFault
and will contain properties for exposing debugging information such as the Message
, StackTrace
, Source
and Target
. The values for these properties will be supplied from the Exception
.
using System.Runtime.Serialization;
namespace Common
{
[DataContract]
public class UnexpectedServiceFault
{
[DataMember]
public string ErrorMessage { get; set; }
[DataMember]
public string StackTrace { get; set; }
[DataMember]
public string Target { get; set; }
[DataMember]
public string Source { get; set; }
}
}
Our service code uses the UnexpectedServiceFault
class as in the example code below.
try
{
}
catch (Exception ex)
{
throw new FaultException<UnexpectedServiceFault>(
new UnexpectedServiceFault { ErrorMessage = ex.Message,
Source = ex.Source, StackTrace = ex.StackTrace, Target = ex.TargetSite.ToString() },
new FaultReason(string.Format(CultureInfo.InvariantCulture,
"{0}", "Service fault exception")));
}
We have now defined how to throw meaningful exception information back to the WCF client. The next step is to catch the exceptions on the client.
Catching a WCF Service Exception on the Client
The following example code shows how our WCF client application can catch the exceptions thrown by the WCF service.
try
{
}
catch (TimeoutException ex)
{
Console.WriteLine("Service has timed out");
Console.WriteLine(ex.Message);
}
catch (FaultException<UnexpectedServiceFault> ex)
{
Console.WriteLine("Service error occurred: {0}", ex.Message);
Console.WriteLine("service message: {0}", ex.Detail.ErrorMessage);
Console.WriteLine("source: {0}", ex.Detail.Source);
Console.WriteLine("target: {0}", ex.Detail.Target);
Console.WriteLine("stack trace: {0}", ex.Detail.StackTrace);
}
catch (FaultException ex)
{
Console.WriteLine("Service error occurred: {0}", ex.Message);
}
catch (CommunicationException ex)
{
Console.WriteLine("Communications error occurred: {0}", ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Error has occurred");
Console.WriteLine(ex.Message);
}
The WCF client code checks for both a FaultException<UnexpectedServiceFault>
and FaultException
exceptions (in that order). The WCF service may therefore throw general exceptions as well as more specific exceptions, and both will be caught and handled by the WCF client.
Summary
I use this technique in all my WCF service applications. Once you have defined your <TDetail>
classes, you can add as much information to your exceptions as necessary, and handle them on the client in any manner that is appropriate. Feel free to leave a comment if you would like me to further elaborate on anything within this tip.