In talking to people about WCF, I've noticed that a lot of people, new to WCF and not, have not realized that WCF has the ability to send rich fault information back to the client by using a Fault Contract. It's very easy. In fact, you can do it in four steps...
Step 1: Admit your faults
The first thing we need to do is determine what kind of information we want to return back to the client and create a data contract that contains that information. You may have several kinds of fault classes, and you should so that you can return specific types of information for specific faults. Kinda like why there isn't just one Exception class in .NET. Although these are data contracts, you should not use them for anything other than sending faults back to the client. Here's a sample fault class:
[DataContract]
public class MyFault
{
private string _message = string.Empty;
public MyFault(string message)
{
_message = message;
}
[DataMember]
public string Message
{
get
{
return _message;
}
set
{
_message = value;
}
}
}
Like any .NET class, you can create a fault class the inherits from another .NET class, BUT that class also needs to be a data contract and be a known type.
Step 2: Find where you may go wrong
Next, we need to let WCF know where these faults might be coming from. This is done at the operation in the service contract. You simply use a FaultContract attribute which lets WCF know what kind of possible fault class to expect:
[ServiceContract]
public interface IFaultyService
{
[OperationContract]
[FaultContract(typeof(MyFault))]
void ThrowMeAnError();
}
You can specify as many faults for the operation as you like, simply add more FaultContract attributes:
[ServiceContract]
public interface IFaultyService
{
[OperationContract]
[FaultContract(typeof(MyFault))]
[FaultContract(typeof(MyOtherFault))]
[FaultContract(typeof(YetAnotherFault))]
void ThrowMeAnError();
}
Step 3: The throw...
Throwing faults from a service is a little different than throwing them from C# or VB code. We need to enclose our fault class in a FaultException<T>, which represents a SOAP exception. This is just as easy as the other steps:
throw new FaultException<MyFault>(new MyFault("This is a fault"), "Demonstration");
The second argument, in this case "Demonstration" is the reason for the fault. And yes, "reason" is the attribute name of the fault too. This can either be a string or a FaultReason object which allows you to send more detailed information about the exception.
Step 4: ... and the catch
If you've done everything correctly, when you generate a reference to your service, in addition to the normal proxy information, you should have a class representing the data contract you created for your fault class. Catching this exception on the client side is similar to throwing it on the server side. It will come wrapped in a FaultException<T> and you will need to specify that to catch it correctly:
.
.
.
catch(FaultException<MyFault> itsMyFault)
{
Console.WriteLine("MyFault was caught");
Console.WriteLine(string.Format(@"Message: {0}", itsMyFault.Detail.Message));
Console.WriteLine(string.Format(@"Reason: {0}", itsMyFault.Reason));
}
.
.
.
As you can see, getting the reason is pretty straight forward. Our message, and any other field me might have added, will be accessible as a member of the Detail child object of our fault.
That's it. See, nothing to it.
Actually, this rabbit hole does go a little deeper, but this should be enough to get you all started with capturing rich error information from your services.
Print | posted on Thursday, April 17, 2008 7:52 PM