I revisited the handling of exceptions in our WCF services recently.
By default, we don't pass exception details back to the caller
(includeExceptionDetailInFaults is set to False); well, except maybe in DEBUG builds.
Anyway, it turned out that we need a bit more details when one of the service calls fails.
The obvious solution is to implement fault contracts - something similar to what is described
on Microsoft's pages (for instance, here).
The implementation is pretty easy and straightforward;
And I wouldn't be writing this text if not for one tiny gotcha (tiny or not tiny - I wasted several hours on this).
Consider the code below - code on the server-side of the service.
and let's assume that we have a class that will be passed as the fault - MathFault
(just like in the document referenced above).
Try
' server-side implementation of the service
. . .
Catch ex As DivideByZeroException
' do whatever you need to do on the server-side to handle this exception
. . .
' and pass the fault to the client (as a strongly-typed object):
Dim mathFault As New MathFault()
mathFault.ProblemType = "Division by zero."
Throw New FaultException(Of MathFault)(mathFault)
End Try
The problem is that, if you debug this code, and an exception is thrown in the Try..Catch block,
a new exception will be thrown in the Throw New line (the last line before the End Try):
FaultException'1 was unhandled by user code
The creator of this fault did not specify a Reason.
Come again? I just did what the MSDN documentation suggested!
After reading a bit more, I added a Reason (a text description of the fault) to the original Throw New code...
This fixed the exception, but something kept nagging me - I didn't need the text reason for anything -
already had the fault type itself and the description in ProblemType...
So, first, it turns out that you can safely ignore this exception when executing your code.
The exception is just an internal, to WCF, mechanism to handle those faults that... don't provide a SOAP text reason for the fault!
Nothing more, nothing less. The exception is reported by Visual Studio because it's configured to show all exceptions by default,
but then the exception is handled internally by WCF.
Note that on the client-side, you can get to the fault class (with all its DataMember properties)
via FaultException's Detail property:
Try
' call to the service proxy on the client-side
. . .
Catch ex As FaultException(Of MathFault)
logger.Log(ex.Detail.ProblemType)
End Try
So what's all this about not specifying a Reason?
Well, basically, if the server-side is responsible for providing text descriptions to the caller about encountered problems,
then the Reason is a standard way of passing that information through service calls.
One way to do that is to just add a text message to the FaultException's constructor call:
Try
' server-side implementation of the service
. . .
Catch ex As DivideByZeroException
' do whatever you need to do on the server-side to handle this exception
. . .
' and pass the fault to the client (as a strongly-typed object):
Dim mathFault As New MathFault()
Throw New FaultException(Of MathFault)(mathFault, "Division by zero.")
End Try
This reason is passed as a separate property of the FaultException - Reason:
Try
' call to the service proxy on the client-side
. . .
Catch ex As FaultException(Of MathFault)
logger.Log(ex.Reason)
End Try
Note that we're getting the Reason directly from the exception, not the Detail part of the exception.
And, finally, let's return to the original "The creator of this fault did not specify a Reason." exception.
If a reason is not specified, the only thing that happens is that FaultException's Reason is just set to this default message.
And that's it...
Top
|