I am basically implementing the IErrorHandler
interface to catch all kinds of exceptions from the WCF service and sending it to the client by implementing the ProvideFault
method.
I am facing one critical issue however. All of the exceptions are sent to the client as a FaultException
but this disables the client to handle specific exceptions that he may have defined in the service.
Consider: SomeException
that has been defined and thrown in one of the OperationContract
implementations. When the exception is thrown, its converted to a fault using the following code:
var faultException = new FaultException(error.Message);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
This does send the error as a string, but the client has to catch a general exception like:
try{...}
catch(Exception e){...}
and not:
try{...}
catch(SomeException e){...}
Not only custom exceptions like SomeException, but system exceptions like InvalidOperationException cannot be caught using the above process.
Any ideas as to how to implement this behavior?
In WCF desirable to use special exceptions described as contracts, because your client may not be .NET application which has information about standard .NET exceptions.
For do this you can define the FaultContract
in the your service and then use the FaultException class.
SERVER SIDE
[ServiceContract]
public interface ISampleService
{
[OperationContract]
[FaultContractAttribute(typeof(MyFaultMessage))]
string SampleMethod(string msg);
}
[DataContract]
public class MyFaultMessage
{
public MyFaultMessage(string message)
{
Message = message;
}
[DataMember]
public string Message { get; set; }
}
class SampleService : ISampleService
{
public string SampleMethod(string msg)
{
throw new FaultException<MyFaultMessage>(new MyFaultMessage("An error occurred."));
}
}
In addition, you can specify in the configuration file that the server returns exception details in it's FaultExceptions, but this is not recommended in a production application:
<serviceBehaviors>
<behavior>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
After that, you can rewrite your method for handling exceptions:
var faultException = error as FaultException;
if (faultException == null)
{
//If includeExceptionDetailInFaults = true, the fault exception with details will created by WCF.
return;
}
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
CLIENT:
try
{
_client.SampleMethod();
}
catch (FaultException<MyFaultMessage> e)
{
//Handle
}
catch (FaultException<ExceptionDetail> exception)
{
//Getting original exception detail if includeExceptionDetailInFaults = true
ExceptionDetail exceptionDetail = exception.Detail;
}