Exceptions are technology specific and therefore not suitable to cross the service boundary of SOA compliant services. Another anomaly we identified is that all exceptions, with the exception of FaultException fault the communication channel, resulting in unhappy proxies as a recover and retry is not possible.
What we need are soap faults that are an industry standard for seamless interoperability … WCF, and there is more … delivers the answer to Soap Faults as FaultException, which result in a soap fault and do not fault the communication channel.
The operations which may throw a FaultException, must be decorated with one or more FaultContract attributes, defining the “exact” FaultException:
[OperationContract]
[FaultContract(typeof(DivideByZeroException))]
void FaultDemoFaultSimple();
The service consumer should catch specific FaultExceptions as shown below, prior to the FaultException and CommunicationException types, to be in a position to handle the specified exceptions:
try
{
proxy.FaultDemoFaultSimple();
}
catch ( FaultException<DivideByZeroException> ex)
{
Debug.WriteLine("FE<>: " + ex.ToString());
}
catch ( FaultException ex )
{
Debug.WriteLine("Fault: " + ex.ToString());
}
To test the FaultException concept we defined the following interface:
[ServiceContract()]
public interface IFaultDemoService
{
[OperationContract]
[FaultContract(typeof(DivideByZeroException))]
void FaultDemoFaultSimple();
[OperationContract]
[FaultContract(typeof(DivideByZeroException))]
void FaultDemoFaultException();
[OperationContract]
[FaultContract(typeof(DivideByZeroException))]
void FaultDemoFaultExceptionCodeReason();
[OperationContract]
[FaultContract(typeof(DivideByZeroException))]
void FaultDemoFaultExceptionReason();
[OperationContract]
[FaultContract(typeof(DivideByZeroException))]
void FaultDemoFaultExceptionVerbose();
[OperationContract]
[FaultContract(typeof(DivideByZeroException))]
void FaultDemoException();
}
The service implementation is as follows:
public class FaultDemoService : IFaultDemoService
{
#region IFaultDemoService Members
public void FaultDemoFaultSimple()
{
try
{
int i, j = 0;
i = 1 / j;
}
catch
throw new FaultException("FaultDemoFaultSimple()",
new FaultCode("FDFS Fault Code"),
"FDFS Action");
}
public void FaultDemoFaultException()
{
try
{
int i, j = 0;
i = 1 / j;
}
catch ( DivideByZeroException ex )
throw new FaultException<DivideByZeroException>(ex);
}
public void FaultDemoFaultExceptionReason()
{
try
{
int i, j = 0;
i = 1 / j;
}
catch (DivideByZeroException ex)
throw new FaultException<DivideByZeroException>
(ex,"FDFER Reason");
}
public void FaultDemoFaultExceptionCodeReason()
{
try
{
int i, j = 0;
i = 1 / j;
}
catch (DivideByZeroException ex)
throw new FaultException<DivideByZeroException>
(ex, "FDFER Reason", new FaultCode("FDFER Code"));
}
public void FaultDemoFaultExceptionVerbose()
{
try
{
int i, j = 0;
i = 1 / j;
}
catch (DivideByZeroException ex)
throw new FaultException<DivideByZeroException>
(ex, "FDFER Reason", new FaultCode("FDFER Code"));
}
public void FaultDemoException()
{
int i, j = 0;
i = 1 / j;
}
#endregion
}
The test client is coded as:
static void Main(string[] args)
{
service.FaultDemoServiceClient proxy =
new FaultDemoProgram.service.FaultDemoServiceClient();
Debug.WriteLine(">");
Debug.WriteLine("FaultDemoFaultSimple -------------------------");
Debug.WriteLine("1a... Channel: " +
proxy.InnerChannel.State.ToString());
try { proxy.FaultDemoFaultSimple(); }
catch (FaultException<DivideByZeroException> ex)
Debug.WriteLine("FE<>: " + ex.ToString());
catch (Exception ex)
Debug.WriteLine(ex.ToString());
Debug.WriteLine("1b... Channel: " +
proxy.InnerChannel.State.ToString());
Debug.WriteLine(">");
Debug.WriteLine("FaultDemoFaultException -----------------------");
Debug.WriteLine("2a... Channel: " +
proxy.InnerChannel.State.ToString());
try { proxy.FaultDemoFaultException(); }
catch (FaultException<DivideByZeroException> ex)
Debug.WriteLine("FE<>: " + ex.ToString());
catch (Exception ex)
Debug.WriteLine(ex.ToString());
Debug.WriteLine("2b... Channel: " +
proxy.InnerChannel.State.ToString());
Debug.WriteLine(">");
Debug.WriteLine("FaultDemoFaultExceptionReason ----------------");
Debug.WriteLine("3a... Channel: " +
proxy.InnerChannel.State.ToString());
try { proxy.FaultDemoFaultExceptionReason(); }
catch (FaultException<DivideByZeroException> ex)
Debug.WriteLine("FE<>: " + ex.ToString());
catch (Exception ex)
Debug.WriteLine(ex.ToString());
Debug.WriteLine("3b... Channel: " +
proxy.InnerChannel.State.ToString());
Debug.WriteLine(">");
Debug.WriteLine("FaultDemoFaultExceptionCodeReason ------------");
Debug.WriteLine("4a... Channel: " +
proxy.InnerChannel.State.ToString());
try { proxy.FaultDemoFaultExceptionCodeReason(); }
catch (FaultException<DivideByZeroException> ex)
Debug.WriteLine("FE<>: " + ex.ToString());
catch (Exception ex)
Debug.WriteLine(ex.ToString());
Debug.WriteLine("4b... Channel: " +
proxy.InnerChannel.State.ToString());
Debug.WriteLine(">");
Debug.WriteLine("FaultDemoFaultExceptionVerbose ---------------");
Debug.WriteLine("4a... Channel: " +
proxy.InnerChannel.State.ToString());
try { proxy.FaultDemoFaultExceptionVerbose(); }
catch (FaultException<DivideByZeroException> ex)
Debug.WriteLine("FE<>: " + ex.ToString());
catch (Exception ex)
Debug.WriteLine(ex.ToString());
Debug.WriteLine("4b... Channel: " +
proxy.InnerChannel.State.ToString());
Debug.WriteLine(">");
Debug.WriteLine("FaultDemoException ---------------------------");
Debug.WriteLine("5a... Channel: " +
proxy.InnerChannel.State.ToString());
try { proxy.FaultDemoException(); }
catch (FaultException<DivideByZeroException> ex)
Debug.WriteLine("FE<>: " + ex.ToString());
catch (Exception ex)
Debug.WriteLine(ex.ToString());
Debug.WriteLine("5b... Channel: " +
proxy.InnerChannel.State.ToString());
Console.ReadLine();
}
So what will the resultant output be you may ask … well here we go, whereby we added some inline comments demarcated in []’s:
1b... Channel: Opened
>
FaultDemoFaultException ----------------------------------------
2a... Channel: Opened
A first chance exception of type 'System.ServiceModel.FaultException`1' occurred in mscorlib.dll
[NOTE that we caught the specific DivideByZero FaultException]
FE<>: System.ServiceModel.FaultException`1[System.DivideByZeroException]: The creator of this fault did not specify a Reason. (Fault Detail is equal to System.DivideByZeroException: Attempted to divide by zero.
at FaultDemoService.FaultDemoFaultException() in c:\Working\FaultDemo\FaultDemoService\App_Code\Service.cs:line 58).
2b... Channel: Opened
[NOTE that the channel is still open after the fault was caught]
>
FaultDemoFaultExceptionReason ----------------------------------------
3a... Channel: Opened
A first chance exception of type 'System.ServiceModel.FaultException`1' occurred in mscorlib.dll
FE<>: System.ServiceModel.FaultException`1[System.DivideByZeroException]: FDFER Reason (Fault Detail is equal to System.DivideByZeroException: Attempted to divide by zero.
at FaultDemoService.FaultDemoFaultExceptionReason() in c:\Working\FaultDemo\FaultDemoService\App_Code\Service.cs:line 71).
3b... Channel: Opened
>
FaultDemoFaultExceptionCodeReason ----------------------------------------
4a... Channel: Opened
A first chance exception of type 'System.ServiceModel.FaultException`1' occurred in mscorlib.dll
FE<>: System.ServiceModel.FaultException`1[System.DivideByZeroException]: FDFER Reason (Fault Detail is equal to System.DivideByZeroException: Attempted to divide by zero.
at FaultDemoService.FaultDemoFaultExceptionCodeReason() in c:\Working\FaultDemo\FaultDemoService\App_Code\Service.cs:line 84).
4b... Channel: Opened
>
FaultDemoFaultExceptionVerbose ----------------------------------------
4a... Channel: Opened
A first chance exception of type 'System.ServiceModel.FaultException`1' occurred in mscorlib.dll
FE<>: System.ServiceModel.FaultException`1[System.DivideByZeroException]: FDFER Reason (Fault Detail is equal to System.DivideByZeroException: Attempted to divide by zero.
at FaultDemoService.FaultDemoFaultExceptionVerbose() in c:\Working\FaultDemo\FaultDemoService\App_Code\Service.cs:line 97).
4b... Channel: Opened
>
FaultDemoException ----------------------------------------
5a... Channel: Opened
[NOTE that the channel is still open before the exception was caught]
A first chance exception of type 'System.ServiceModel.FaultException' occurred in mscorlib.dll
System.ServiceModel.FaultException: The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at FaultDemoProgram.service.IFaultDemoService.FaultDemoException()
at FaultDemoProgram.service.FaultDemoServiceClient.FaultDemoException() in C:\Working\FaultDemo\FaultDemoProgram\Service References\service.cs:line 112
at FaultDemoProgram.Program.Main(String[] args) in C:\Working\FaultDemo\FaultDemoProgram\Program.cs:line 88
5b... Channel: Faulted
[NOTE that the channel is faulted after the exception was caught. It proves that throwing a .NET exception is fatal to the channel, whereas a FaultException is handled gracefully.]
Hope this snippet of the DRP2007 is of value to you when dealing with the impossible, the unthinkable … the exception.