共用方式為


WCF error handling and some best practices

I put together the following brief description of WCF Error Handling and some possible best practices for a customer. You may also find it useful:

 

There are 4 sets of errors that clients can expect:

 

Invalid configuration:whenbindings, behaviors or any other configs are in conflict with some other settings.

Communication errors: These are the usual errors caused as the result of network communication issues such as incorrect or unreachable addresses and the unavailability of a network connection. You may receive a CommunicationException as a result this.

Service faults: By default all service side exceptions are sent to the client as FaultException.

Proxy or channel state errors: These types of errors are raised when the channel or the proxy is not in a correct state to allow for communications. For example the proxy could be in the Faulted state and attempting to use that proxy will throw an exception.

 

Why Faults instead of Exceptions?

As you are aware, WCF mainly deals with SOAP Faults instead of Exception. Here is a blog entry I wrote highlighting some the reasons on why we use faults instead of exception.

In short, a SOAP Fault provides an adequate mapping between service exceptions and their equivalent on the client.

 

Should you throw a CLR exception or should you prefer a FaultException or its derivative FaultException<T>?

As mentioned above, all service side exceptions (non FaultException derived ones) are automatically converted to a FaultException. A FaultException in itself however does not provide much information regarding the problem. It is also not possible to distinguish between different types of exceptions at the client-end as all exceptions are automatically converted to a generic FaultException. Here is an example of a case that a simple System.Exception was thrown but the client received a FaultException with no more information:

 

An exception of type 'FaultException' was caught...

Message: 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.

 

If a FaultException was used, some more information regarding the exception could have been sent to the client in a form of a FaultReason. In instances where an unhandled non-FaultException is thrown, one of the following may happen based on the instance management settings of your service:

 

For per-call services:

The service instance is disposed and the proxy throws a FaultException on the client side. All exceptions of this type will fault the channel so that the same proxy can no longer be uses. In fact, attempting to reuse or even dispose the proxy may throw the following:

 

An exception of type 'CommunicationObjectFaultedException' was caught...

Message: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.

 

For sessionful services:

The session is terminated, the service instance is disposed and the channel will be in the faulted state, thus the proxy cannot be reused.

 

For singleton services:

The channel is faulted and the same proxy cannot be used. However the singleton instance will live on.

 

In all cases mentioned above, the channel moves to the Faulted state. This is however not the case when a FaultException or a FaultException<T> is used. Therefore it is strongly recommended to throw a FaultException or one of its derivatives.

 

FaultException or FaultException<T>?

A FaultException in itself does not allow the service to provide detailed information regarding the exception to the client. In fact an instance of a FaultReason is as much information as you can send to the client. A FaultReason allows for localised versions of a message to be sent to the client.

 

Also, using a FaultException, it would be hard to distinguish between different exceptions at the client-end. In scenarios that you need to distinguish between exceptions and/or provide more information regarding the error, you can use a FaultException<T>. The constructor for this class takes an instance of T. T needs to be a DataContract or at least a serializable type. The instance of T will provide more information regarding the Fault.

 

At this point it is worth mentioning that all FaultException<T> instances are automatically converted to the simpler FaultException type if no FaultContract matching the type T is defined for the operation in question. Therefore when using a FaultException<T>, it is advisable to use a FaultContract. Of course it is possible to have multiple FaultContracts defined for an operation.

 

Do you need a FaultContract?

The answer is NO but read on. When throwing a FaultException or one of its derivatives, if no FaultContracts are specified, they are all converted to a simple FaultException. Use FaultContracts however when you are using FaultException<T> and you need the detailed information provided by the instance of T or you need to be able to distinguish between different exception types.

 

BTW, FaultContracts are published as part of the metadata for the service. Therefore T will have a representation at the client-end. In an unlikely scenario that the representation of T on the client has been modified manually and no longer matched T on the service, an instance of FaultException is thrown by the client.

 

How about one-way operations?

It is not possible to specify a FaultContract for one-way operations.

 

For debugging and diagnostics only:

It is possible to enable exception details to be sent to the client. This can be done using the IncludeExceptionDetailInFaults property of the service behaviour. However the use of this option is only recommended for debugging or problem diagnosis scenarios.

 

An example of handling errors at the client:

 

try

{

  proxy.SomeOperation();

}

catch (FaultException<MyFaultInfo> ex)

{

  // only if a fault contract was specified

}

catch (FaultException ex)

{

  // any other faults

}

catch (CommunicationException ex)

{

  // any communication errors?

}

 

An interesting and useful WCF extensibility point for error handling:

WCF is a very extensible framework. You can explicitly control the behaviour of your application when an exception is thrown.

You can

- Decide to send a fault to the client or not,

- Replace an exception with a fault,

- Replace a fault with another fault,

- Perform logging,

- Perform other custom activities

 

In order to utilise this extensibility feature, you need to implement the IErrorHandler interface. You will then need to install your custom error handler by adding it to the ErrorHandlers property of the channel dispatchers for your service. It is possible to have more than one error handler and they are called in the order they are added to this collection.

 

IErrorHandler introduces two very interesting methods:

 

public interface IErrorHandler

{

  bool HandleError(Exception error);

  void ProvideFault(Exception error,

       MessageVersion version, ref Message fault);

}

 

Implement ProvideFault to control the fault message that is sent to the client. This method is called regardless of the type of the exception thrown by an operation in your service. If no operation is performed here, WCF assumes its default behaviour and continues as if there were no custom error handlers in place.

 

Using the ProvideFault method, it is possible to provide a new fault. Here is an example:

 

public void ProvideFault(Exception error,

      MessageVersion version, ref Message fault)

{

  FaultException newEx = new FaultException();

  MessageFault msgFault = newEx.CreateMessageFault();

  fault = Message.CreateMessage(version, msgFault, newEx.Action);

}

 

One area that you could perhaps use this approach is when you want to create a central place for converting exceptions to faults before they are sent to the client (ensuring that the instance is not disposed and the channel is not moved to the Faulted state).

 

The IErrorHandler.HandleError method on the other hand is usually used to implement error-related behaviours such as error logging, system notifications, shutting down the application, and so on. This would be my recommended place to add logging capabilities.

Correction: IErrorHandler.HandleError can be called at multiple places inside the service, and depending on where the error is thrown, the HandleError method may or may not be called by the same thread as the operation. In other words, WCF does not make any guarantees on which thread the HandleError call may be processed.

 

Use of Exception Shielding and the Exception Handling Application Block:

One other recommended approach for dealing with service based exceptions is through the use of Exception Handling Application Block as part of the Enterprise application Library. In his blog, Guy Burstein describes how you can use EHAB with WCF.

 

Which option should you use?

It all depends on your requirements. Application blocks aim to incorporate commonly used best practices and provide a common approach for exception handling throughout your application. On the other hand, custom error handlers and fault contracts can also be very useful. For instance custom error handlers provide an excellent opportunity to automatically promote all exceptions to FaultExceptions and also to add logging capabilities to your application.

Comments

  • Anonymous
    January 25, 2008
    PingBack from http://msdnrss.thecoderblogs.com/2008/01/25/wcf-error-handling-and-some-best-practices/

  • Anonymous
    January 26, 2008
    The link behind "In his blog, Guy Burstein" is wrong ;)

  • Anonymous
    January 26, 2008
    Hi Michael, It is now fixed. Thanks for the heads up Pedram

  • Anonymous
    January 26, 2008
    Me again.... Beside your great post, i'd like to ask, if its possible to access a WCF written service from a .net 1.1 application without using (ws)httpbinding and having features such like windows authentication? Could you handle this scenario in your next blog entry?

  • Anonymous
    January 29, 2010
    When WCF goes into a faulted state, is there a way of resetting it.  IIS Reset and Reboot do not work.  The state is still faulted. Also, when running the code in debug mode, it works fine.  Only when calling outside of VS does it fail.  

  • Anonymous
    March 04, 2010
    Great article. Here is similar article from MSDN where you can find some more: [URL]http://msdn.microsoft.com/en-us/library/cc949036.aspx[/URL] But this one is better :)

  • Anonymous
    November 18, 2010
    The comment has been removed

  • Anonymous
    June 08, 2011
    really good

  • Anonymous
    December 19, 2011
    Hi, Great post on WCF error handling. I think there is only one thing missing and that how to handle the situation when you have a channel in a faulted state. There is enough documentation on the internet have to fix this, but i would make this post complete.

  • Anonymous
    January 18, 2012
    I'm not sure if I fully agree that the only thing you can pass using FaultException is Fault Reason. There is also Fault Code which you can later use on the client to differentiate different types of Faults. catch (FaultException faultEx) {  switch (faultEx.Code.Name)  {    case "ConnectionFault":      MessageBox.Show(faultEx.Message +        "nn Try again later.", "Connection problem");      break;    case "DataReaderFault":      MessageBox.Show(faultEx.Message +        "nn Contact the administrator.", "Data problem");      break;    default:      MessageBox.Show(faultEx.Message +        "nn Contact the administrator.", "Unknown problem");      break;  } } msdn.microsoft.com/.../ee942778.aspx

  • Anonymous
    July 29, 2012
    Thank u. It will be the correct answer.