Introduction
After playing around with Silverlight for a while, I got very frustrated from a basic feature I felt was missing... The ability to catch exceptions thrown by a WCF service from within a Silverlight app. So, I put together some useful re-usable code libraries to help solve this issue.
Background
Exception handling has been around for donkey's years, and is a key feature of the .NET Framework. This is all great when you are running your code locally within the bounds of .NET, but once your solution grows and goes enterprise, you enter the realm of services and channels and other exciting stuff where the .NET Framework doesn't have control over everything (especially when it tries to conform to standards).
So, in the world of WCF services, we have what's known as Faults. Simply putting it, instead of throwing an Exception in a service, we throw a special FaulException
which the WCF Dispatcher handles and wraps up into our response message. This, when received by our client, gets unwrapped and thrown to the calling client method.
If we want to send extra info along with our fault, we just have to create a serializable class (using either the [Serializable]
or [DataContract]
attribute), and then throw the generic FaulException<T>
(where T
is our serializable class) and send our class into the constructor. For the client to catch this unique exception, a [FaultContractAttribute(typeof(T))]
(where T
is our serializable class) must be declared above the operation contract that may throw this fault. The client proxy created by WCF will read the fault header in the returning message and try to desirialize it into a generic FaultException
matching the type we defined in the contract's FaultContractAttribute
.
So, what's the issue?
Well, as many of you might have realized by now, all the above does not apply to Silverlight. Although it is in its second version already, Microsoft has not implemented the fault handling mechanism that is available in other types of .NET platforms.
To make things worse, if we throw an exception in the service, Silverlight will return a very weird HTTP code exception telling us the file was not found... and so at this point, you probably go, Hugh?
Silverlight and Browsers
Since the Silverlight runtime is hosted within a browser, it relies on the browser to handle all its network requests. That's part of the reason why the only binding you can use in Silverlight is basic HTTP. When an exception or fault is thrown by the service, the HTTP response code is not a regular 200 OK response, rather a 40X error response. This is intercepted and handled by the browser before Silverlight gets a chance to examine the entire response header, and the response passed to Silverlight from the browser is simply one that says... Something went wrong.
How do we fix this issue?
I have seen several workarounds posted on the web where the exceptions are wrapped into a class deriving from the method's return type (very ugly). But, this requires you to modify all your returning DataContracts to include an ExtraException
property, and it won't work on primitive return values.
So, the way we will go about this is as follows:
- On the client end, create a proxy wrapper class that all the service calls will be routed through. This proxy class will add an extra attribute to the sent message header stating that the client cannot handle faults properly.
- On the service end, implement an Operation Invoker that will implement the
IOperationInvoker
interface that will be called by the WCF Dispatcher instead of the service's method. Our Operation invoker will wrap the underlying service method with a simple try/catch
block, and if an exception is thrown, it will check for the special attribute the client may have set telling us it can't handle faults/exceptions. If the attribute exists, return null
to the dispatcher (this will result in the service returning a 200 Response to the browser), but add some extra fault info to the message header containing the info of the exception.
- Back on the client proxy, test to see if the response message has any fault info in its header. If it does, deserialize it and throw it at the client; otherwise, return the message content.
IOperation who? (Taking control from the dispatcher)
I mentioned the fact that on the service end, we will implement an IOperationInvoker
. Without going into too much detail, what we want to do is tell the WCF dispatcher that we are going to handle requests made to a certain method (Operation) within a service contract. We do this by registering an IOperationInvoker
item with the dispatcher for a specific method. From here on, whenever a call comes over the wire for WCF to invoke the registered Operation, instead of calling the method implementing the contract, the WCF dispatcher will call our method. This lets us take control of the message before we send it on to the original method, and to modify the returned message before it gets sent over the wire.
Making it simple
To create a simple reusable IOperationInvoker
, we can create a class deriving from the .NET System.Attribute
class that implements the IOperationBehavior
interface. We can then add this attribute to any service method that implements an operation in our service contract, and the dispatcher will call on it to apply any custom dispatch behaviors. When the call comes in, we will set the operation's invoker to our custom IOperationInvoker
class, and that will route the calls to our implementation.
In the code provided, the attribute is defined as WCFHelpers.Service.FaultHandlingAttribute
which sets the invoker to a new WCFHelpers.Service.FaultHandlingInvoker
class.
We can then add this attribute to any method in our service:
public class SimpleService : ISimpleService
{
#region ISimpleService Members
[FaultHandling]
public SimpleInfo GetSimpleInfoDirect(int Id)
{
}
#endregion
}
The FaultDetail class
To send our custom exception info down the wire, the WCFHelpers.Client
namespace defines a FaultDetail
class. This is kind of a slim-down exception class that contains:
- A message property.
- A type name (representing the original exception's type).
- An
InnerFault
property that holds info on any InnerException
info.
- An
OuterFault
which is kind of like WCF's FaultException<T>
's Detail
property which allows you to add any extra info you want to the fault.
The type has a constructor that accepts an Exception
item and can initialize itself from it. Or alternatively, you can instantiate a blank one instead and set the properties yourself.
Sending the Fault information down the wire
As I mention earlier, once we catch an exception in our custom operation invoker class, we must decide how to send it down the wire.
To make the code as generic as possible, the invoker does the following once an exception is caught:
- If the client can handle faults (a Windows Forms app, for example), then we check the type of the exception thrown. If it derives from the WCF
FaultException
, we simply re-throw it and let WCF handle the serialization of the Fault. If it is not a proper WCF FaultException
, we create a generic wrapper FaultException<WCFHelpers.Client.FaultDetail>
and instantiate it with a new FaultDetail
class created from the original exception.
- If the client can't handle faults (like Silverlight), then we instantiate a new
FaultDetail
class created from the original exception (or alternatively, extract the FaultDetail
info from a FaultDetailException
if one was thrown), and serialize it into the outgoing message's header.
Since the invoker differentiates between different exception types, you can send most info down the wire by throwing the WCFHelpers.Client.FaultDetailException
and sending a custom FaultDetail
class into the exception's constructor. This will be converted into a FaultException<WCFHelpers.Client.FaultDetail>
exception if the client supports faults, or alternatively serialize the custom FaultDetail
property and send it down to a non supporting client.
A tip to contract developers: Since all exceptions are converted to the WCF FaultException<WCFHelpers.Client.FaultDetail>
for clients that support Fault handling, you can add this type as a FaultContractAttribute
to your contract's operation.
The client proxy
The code is pretty well documented, but I will explain in short the client proxy. The proxy is defined in WCFHelpers.Client.ObjectClient<TServiceContract>
, where TServiceContract
is the contract type. It exposes two public methods:
Invoke
: This is a fairly straightforward method that calls a service operation synchronously and returns the result to the caller. If an exception/fault was sent down the wire, the method throws it up to the caller.
Begin
: This method wraps async operation calls where the methods are called Beginxxx
and Endxxx
, and implement the standard .NET Async methodology. This method accepts an extra callback delegate of type EventHandler<ClientEventArgs>
that will be called when the method completes. The ClientEventArgs
will contain the method's result in its Result
property, and if an exception was sent down the wire, it will be set to the Exception
member.
The proxy class also exposes a public bool
property named HandlesFaults
(surprise surprise!). When this property is set to true
, any call made through the proxy will have an extra header added to the message stating that this client can't handle faults in the conventional way. It is up to our custom service operation invoker to intercept this header and to pack any exception accordingly.
Using the code
The helper classes are divided into two separate assemblies:
To use the code, the following approach is recommended:
On the service end, add a FaultHandling
attribute to any method that may throw an exception. Also add a [FaltContract(typeof(FaultDetail))]
attribute to the contract definitions of these methods.
On the contract:
[ServiceContract]
public interface ISimpleService
{
[OperationContract]
[FaultContract(typeof(WCFHelpers.Client.FaultDetail))]
SimpleInfo GetSimpleInfoDirect(int Id);
[OperationContract(AsyncPattern=true)]
[FaultContract(typeof(WCFHelpers.Client.FaultDetail))]
IAsyncResult BeginGetSimpleInfo(int Id, AsyncCallback Callback, object state);
[FaultContract(typeof(WCFHelpers.Client.FaultDetail))]
SimpleInfo EndGetSimpleInfo(IAsyncResult result);
}
At the service:
public class SimpleService : ISimpleService
{
#region ISimpleService Members
[FaultHandling]
public SimpleInfo GetSimpleInfoDirect(int Id)
{
}
[FaultHandling]
public IAsyncResult BeginGetSimpleInfo(int Id, AsyncCallback Callback, object state)
{
return WCFHelpers.Service.Async.AsyncResult<SimpleInfo>.BeginAsync(
delegate { return GetSimpleInfoDirect(Id); }, Callback, state);
}
[FaultHandling]
public SimpleInfo EndGetSimpleInfo(IAsyncResult result)
{
WCFHelpers.Service.Async.AsyncResult<SimpleInfo> simpleResult =
result as WCFHelpers.Service.Async.AsyncResult<SimpleInfo>;
return simpleResult.EndInvoke();
}
#endregion
}
Note the use of the AsyncResult<SimpleInfo>
to strongly type our Async pattern method.
At the client
At the client end, the simplest use is to derive a class from WCFHelpers.Client.ObjectClient<TServiceContract>
and add a strongly typed method for each operation in the contract:
public class SimpleClient: WCFHelpers.Client.ObjectClient<ISimpleService>
{
public SimpleClient(String endpointConfigurationName)
: base(endpointConfigurationName)
{
base.HandlesFaults = false;
}
public SimpleClient(Binding binding, EndpointAddress address)
: base(binding, address)
{
base.HandlesFaults = false;
}
public void GetSimpleInfoAsync(int Id,
EventHandler<WCFHelpers.Client.ClientEventArgs> callback, object State)
{
base.Begin("GetSimpleInfo", callback, State, Id);
}
public SimpleInfo GetSimpleInfoDirect(int Id)
{
return base.Invoke("GetSimpleInfoDirect", Id) as SimpleInfo;
}
}
Some annoying issues
As all good things usually do, so does this solution present some annoyances.
The first is re-using the WCFHelpers.Client.dll assembly in both Silverlight and standard WinForms clients. If you've ever tried adding a reference to a non-Silverlight assembly into a Silverlight app using Visual Studio, you've probably noticed it won't work. This is because the base runtime assemblies (System.dll, mscorelib.dll etc.) used by Silverlight are different from those used in the standard .NET environment, and so when Silverlight wants to load a regular .NET assembly into memory, the assembly is looking for the wrong runtime references and won't load. Luckily, there are ways to fix this, and you can read my article Converting .NET Assemblies to Silverlight Assemblies to see how to solve this. The project provided in the download contains a post-build command that generates a Silverlight compatible assembly version of the WCFHelpers.Client.dll and places it in the bin\Silverlight directory (as explained in the above article).
Note: this same mechanism is applied to the Contract assembly in the test application to allow the Contract definitions to be shared between the Silverlight test and the WinForms test clients.
The second annoying issue ranks much higher on the "bang-your-head-against-a-keyboard" scale. If you implement all the above code as explained, but throw from within your service a WCF FaultException
, you will probably notice that you are back to square one, where your faults are not being sent back to your Silverlight client. This is due to the fact that the WCF dispatcher handles FaultException
s internally and then re-throws the exception back up to our custom Operation Invoker. By the time we handle the exception and swap it for a simple header in the outgoing message, the dispatcher has already flagged the operation as Faulted and will prepare a Fault reply even though we handled the exception. The only workaround I have at this point is simply not to throw a WCF FaultException
directly, rather throw a FaultDetailException
(or any other .NET Exception not deriving from FaultException
or SecurityException
). These faults are not intercepted by the Dispatcher, and when caught by our custom Operation Invoker, they will be re-thrown as a WCF FaultException<FaultDetail>
if the client supports fault handling.
Points of interest
The WCFHelpers.Service.AsyncResult<TResult>
contains a static method named BeginAsync
. This is a useful re-usable piece of code to simplifying Async call pattern development for services. It is well documented, so take a look at it...
Conclusion
For one, I didn't think this article would be so long when I started to write it; otherwise, I probably would have never had the patience to sit down and write it.
In any case, this is the methodology I have been implementing and it holds firm. If you have come around this issue and you have taken the solution to different directions, drop me a line and I'll be glad to get some new insights.
I have included two test clients, one Silverlight and the other a WinForms client. Both do exactly the same, only the WinForms client uses the traditional Fault contracting exposed by WCF, and the Silverlight version uses the alternate mechanism explained. In both test clients, pressing the button will send a random number to the service. The service returns valid info for some of the numbers, and throws exceptions for the others, so if you don't get an exception fired immediately, just push the button a few more times and eventually it will fire an exception.
History
A great channel, but none yet for this article.