Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C#

Catch and Handle WCF Service Exceptions in Silverlight

Rate me:
Please Sign up or sign in to vote.
4.94/5 (18 votes)
22 Jan 2009CPOL12 min read 105.1K   1.1K   52   14
A reusable set of code to enable service thrown exception handling in Silverlight.

Image 1

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:

  1. 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.
  2. 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.
  3. 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:

C#
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:

  1. A message property.
  2. A type name (representing the original exception's type).
  3. An InnerFault property that holds info on any InnerException info.
  4. 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:

  • WCFHelpers.Client.dll: Contains the client namespace, and holds the client proxy class (ObjectClient<TServiceContract>), the FaultDetail and FualtDeatilException classes, and a static class called Global that contains methods to read and write faults to the message headers.
  • WCFHelpers.Service.dll: Contains the service namespace, and holds our custom operation invoker along with the FaultHandlingAttribute that is to be applied to any service implementer method that may have a client that does not support fault handling. The assembly also exposes an Async namespace (WCFHelpers.Service.Async) that contains two reusable classes to help implement the Async call pattern on your service.
  • Note: this assembly references the WCFHelpers.Client.dll assembly.

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:

C#
[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:

C#
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>;
         //This will thro an exception if one has occurd in the initial execution
         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:

C#
public class SimpleClient: WCFHelpers.Client.ObjectClient<ISimpleService>
{
    public SimpleClient(String endpointConfigurationName)
        : base(endpointConfigurationName)
    { 
        //notify the proxy that we don't handle faults
        base.HandlesFaults = false;  
    }
    public SimpleClient(Binding binding, EndpointAddress address)
        : base(binding, address)
    {
        //notify the proxy that we don't handle faults
        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 FaultExceptions 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) VCM Software
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
valamas3-Jan-12 18:29
valamas3-Jan-12 18:29 
GeneralMy vote of 5 Pin
RaviRanjanKr4-Dec-11 0:59
professionalRaviRanjanKr4-Dec-11 0:59 
BugSilverlight enabled WCF service - CommunicationException Pin
csvishal20048-Aug-11 10:02
csvishal20048-Aug-11 10:02 
AnswerThere is a simple work around for this issue. Pin
Duojiao Yan10-Mar-11 20:16
Duojiao Yan10-Mar-11 20:16 
QuestionWhy not just implement IErrorHandler on the service class? Pin
Marc Scheuner4-Aug-09 21:55
professionalMarc Scheuner4-Aug-09 21:55 
GeneralError with SLASM tool Pin
gilesbradshaw3-Jul-09 2:34
gilesbradshaw3-Jul-09 2:34 
GeneralCall from VB code Pin
FaxmanAZ12-May-09 11:06
FaxmanAZ12-May-09 11:06 
GeneralProblem deserialising return type Pin
knbrewer15-Apr-09 2:03
knbrewer15-Apr-09 2:03 
GeneralSolution for Service Excepion for FaultException Error Pin
Ashvin Kanani19-Mar-09 9:40
Ashvin Kanani19-Mar-09 9:40 
RantRe: Solution for Service Excepion for FaultException Error Pin
Suriel Bendahan19-Mar-09 10:19
Suriel Bendahan19-Mar-09 10:19 
QuestionTypo? Pin
Ravi Bhavnani21-Feb-09 6:03
professionalRavi Bhavnani21-Feb-09 6:03 
AnswerRe: Typo? Pin
Suriel Bendahan21-Feb-09 10:21
Suriel Bendahan21-Feb-09 10:21 
GeneralExcellent! Pin
Ravi Bhavnani23-Jan-09 6:52
professionalRavi Bhavnani23-Jan-09 6:52 
GeneralRe: Excellent! Pin
Suriel Bendahan24-Jan-09 12:39
Suriel Bendahan24-Jan-09 12:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.