WCF, or Windows Communication Foundation, was the technology to use in a .NET-focused service-oriented architecture (SOA) in the second half of the 2000’s. Its SOAP approach may have been surpassed by REST principles, but there are still many WCF services out there. Many of them are now considered legacy applications, but they continue to provide important functionalities to companies around the globe.
It’s still important (and possible!) to apply modern-day best practices to them. One of those best practices is correct error handling. In this article, I hope to explain to you what happens with errors in WCF and how you should handle them properly.
Proper WCF error handling involves using best practices on the server and client side. You need to make sure that you properly collect exceptions happening on the server and also return them to the client to handle.
Proper WCF error handling requires implement both best practices.
Anytime an exception is thrown within your application, you should log it to help troubleshoot application problems. Let’s cover a couple of ways to accomplish this so that you can track all WCF exceptions occurring in your application.
Unfortunately, WCF does not have a very simple and straightforward way to do global error handling. You must utilize your own IErrorHandler class and apply it to your WCF services. This is rather simple to do but requires writing a little bit of code.
Steps to implement server-side WCF error handling:
The IErrorHandler implementation must be added to the ErrorHandlers property of the service. There are basically two easy ways of doing this. Both require some code that is too much to post here but you can find it on MSDN or from this CodeProject.com example. Either you create a custom attribute that you can add to your service class, or you create a WCF service behavior that you can then add to your web.config file.
The IErrorHandler interface contains two methods: ProvideFault and HandleError.
ProvideFault is where you can modify an existing fault or suppress it if you want to.
You can suppress the error like this:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { fault = null; }
This will cause the service to send back the non-generic FaultException again, just like in our very first example.
But the interesting bit is when the fault parameter is null. This means an exception occurred in our application that we haven’t yet translated to a FaultException<T>. A typical case is a NullReferenceException. We can still provide a FaultException if we want to. Like this, for example:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { if (fault == null) { var faultException = new FaultException("Something went wrong internally."); var messageFault = faultException.CreateMessageFault(); fault = Message.CreateMessage(version, messageFault, faultException.Action); } }
This results in the following output of our client:
Notice how it’s a non-generic FaultException again. This is because we didn’t use a fault contract here.
The HandleError method is where you can handle the exception that was raised. Typically, this is where you log the error. Return true here to indicate that the error has been handled. If you return false, you signal to WCF that the current session is considered corrupt and should be aborted. In my experience, you mostly want to return true, but your use case may differ.
You can have multiple IErrorHandler implementations for your service. It might be non-intuitive, but it’s important to note that first all ProvideFault methods are called in the order that they were added. The Fault (if not suppressed) is then sent back to the client. And only then are the HandleError methods called.
Retrace can automatically collect all .NET exceptions that are occurring within your application with no code changes. This includes unhandled exceptions in WCF but can also include all thrown exceptions, or first chance exceptions.
Retrace does this via the robust code level profiling that it implements. It allows you to get detailed performance details about your application, including exceptions being thrown. Start your free, two week trial of Retrace today.
There are several challenges with exceptions when you’re working with distributed systems. For example: what happens when an exception occurs in a service that a client depends on? Should it also bubble back to the client? How should the client handle this exception? And in what form should the client expect this exception?
Communication between a WCF-client and a WCF-service can happen over several different transport mechanisms: HTTP, TCP, named pipes, or MSMQ. It doesn’t work exactly the same as the exception system of in-process calls in a .NET application.
Let’s walk through how to use a FaultContract in WCF to better handle exceptions from the calling application.
To better understand what happens with exceptions over WCF, we’ll set up a test project. To get started, you can simply create a new “WCF Service Application” in Visual Studio:
This should give you a service with two methods:
These will do for our use case. Now add a client application by creating a new Console App in the same solution. Start your WCF Service, and then add a Service Reference to the Console App project:
In the next dialog, click “Discover” and choose “Services in Solution.” Click OK and you can now easily create a client for your service:
var client = new Service1Client(); var response1 = client.GetData(12);
I realize this is a simplified example. In most enterprise environments, the client and the service cannot be in the same solution, and often, the service reference is added manually, for various reasons. But bear with me here. This is purely to set up an easy example to show the mechanics of WCF error handling.
I’ve written my client in such a way that it calls both methods and writes the output to the console:
try { var response2 = client.GetDataUsingDataContract(null); Console.WriteLine("Response for GetDataUsingDataContract:\r\n" + $"Boolean value: \"{response2.BoolValue}\"\r\n" + $"String value: \"{response2.StringValue}\""); } catch (Exception e) { Console.WriteLine("Exception for GetDataUsingDataContract:\r\n" + $"Exception type: \"{e.GetType()}\"\r\n" + $"Message: \"{e.Message}\"\r\n"); }
Now it’s time to play around with our service and make it throw some exceptions. I made the methods in the WCF service throw an ArgumentNullException, which gives me the following output:
So even though we threw an ArgumentNullException, our client catches a FaultException. This is because WCF hides the technical details from us, out of security concerns. You might not want to expose too much of the technical inner workings of your WCF service to the outside world.
But let’s, for a brief moment, ignore security concerns and turn on IncludeExceptionDetailInFaults as suggested. You can edit your web.config to do this:
Running the client again now produces the following output:
We now receive a slightly more specific FaultException: the generic FaultException<ExceptionDetail>. This ExceptionDetail class contains the message and stack trace of our original exception (in the Detail property). So it feels like our traditional .NET exception handling, except that we can’t catch this ArgumentNullException, because it isn’t one. It’s a FaultException.
And to get back to the web.config change we made: this might be a security risk as we’re leaking out information about the inner workings of our service.
WCF has a concept of fault contracts to solve our previous issues. Fault contracts allow us to:
First, we need to define a fault contract. Fault contracts are actually just DataContract like any other:
[DataContract] public class NullInputFault { [DataMember] public int ErrorCode { get; set; } }
Next, we have to add a fault contract attribute to any method on our ServiceContract that can throw this exception. In our case, our ServiceContract now looks like this:
[OperationContract] [FaultContract(typeof(NullInputFault))] string GetData(int value); [OperationContract] [FaultContract(typeof(NullInputFault))] CompositeType GetDataUsingDataContract(CompositeType composite);
This allows us to change our client code to:
try { var response2 = client.GetDataUsingDataContract(null); Console.WriteLine("Response for GetDataUsingDataContract:\r\n" + $"Boolean value: \"{response2.BoolValue}\"\r\n" + $"String value: \"{response2.StringValue}\""); } catch (FaultException<NullInputFault> e) { Console.WriteLine($"NullInputFault! Code: {e.Detail.ErrorCode}"); } catch (Exception e) { Console.WriteLine("Exception for GetDataUsingDataContract:\r\n" + $"Exception type: \"{e.GetType()}\"\r\n" + $"Message: \"{e.Message}\"\r\n"); }
We’re now specifically catching the FaultException<NullInputFault> and handling it differently than other exceptions.
Finally, we need to actually throw this FaultException<NullInputFault> instead of our ArgumentNullException:
throw new FaultException<NullInputFault>(new NullInputFault {ErrorCode = 20});
Before running this, make sure you update your service reference. Right-click it in the Solution Explorer and choose the appropriate option. When we run our example now, we get this output:
We’ve established that we can use fault contracts in our WCF service so that you can catch meaningful exceptions in our WCF client. This is akin to writing your own custom exception classes with any properties that you need. If you’re writing a desktop application or a class library, it makes sense to throw meaningful exceptions that other pieces of code can catch. Take this code for example:
public static Unit GetUnitByName(string name) { Unit result = null; if (!TryGetUnitByName(name, out result)) { throw new UnknownUnitException(string.Format("No unit found named '{0}'.", name)); } return result; }
This is taken from an open-source library that I maintain. It should be clear what the code is doing. But the important piece here is that I’m not throwing an ArgumentException or any other typical .NET exception. Instead, I’ve created my own UnknownUnitException so that anyone using the library can catch this exception specifically. If I had thrown a more general exception, users of the library would have to catch this general exception and inspect it to know if it’s an exception they want to handle or not.
This translates perfectly to fault contracts. Fault contracts are the WCF way of doing error handling correctly. Instead of having to catch a FaultException or a FaultException<ExceptionDetail> and inspecting it to know more, WCF clients can catch a specific FaultException<T> where T is your fault contract. They can then handle accordingly, using any extra information that you have provided.
If you’ve been coding along, you may have noticed that the constructor of FaultException allows you to provide a “reason.” This is where you can add some more information, just like how you can provide a message in .NET exceptions. If you don’t provide a reason, the Reason property of the FaultException will be “The creator of this fault did not specify a Reason.”
Incidentally, the Reason and Message properties of a FaultException will be the same. I couldn’t find any confirmation on this, but I believe this is because WCF follows the SOAP specification that defines a Reason element. So you can think of the Reason and Message being one and the same.
Another parameter that you can supply to the FaultException constructor is a FaultCode. The SOAP specification defines some standard fault codes but you can define your own too. Basically, it’s a string that the client can inspect in order to decide what to do with the error. Though you will probably want to look into fault contracts because they are easier to use and enable cleaner code.
This was a lengthy article, but please take away from this that WCF error handling is actually quite simple. Microsoft has done a great job at translating well-known .NET exception concepts to the WCF/SOAP space.
In your application or library, it’s a best practice to create your own custom exceptions. In the same way is it a best practice to create fault contracts and return those to your client. Fault contracts are just data contracts so you should have no trouble defining them. Just don’t forget to add them to the methods on your service interface.
And finally, create a way of catching all exceptions that happen in your WCF service and returning FaultExceptions. This gives you the necessary control to expose only what you want and what the client needs. Use the IErrorHandler interface for this, combined with either an attribute or a service behavior.
If you would like to be a guest contributor to the Stackify blog please reach out to [email protected]