Sunday, January 3, 2010

WCF Generic Error Handling using IErrorHandler

This is one of the series of WCF Blogs I have written. Click here to visit all my WCF Blogs.

Error Handling in WCF using IErrorHandler Interface

Source Code: Download

Exceptions are a critical component of a robust system and can be indicators of a variety of situations. For example, a caller may not have provided correct or complete information to a service, a service may have encountered an issue attempting to complete an operation, or a message may be formatted according to an unsupported version.

In this blog, I will talk about the effect exceptions have in WCF and the features WCF provides for communicating and processing exceptions. I will also describe the difference between exceptions and faults, the ways to create faults to send to a caller, and ways to process exceptions on both the service and caller. Finally, I will describe ways to centralize exception processing, catching unexpected exceptions or performing additional processing on exceptions and faults, such as logging.

A WCF service typically wraps calls to underlying business logic libraries, and as would be expected in any managed code, these libraries may raise standard .NET exceptions to their callers. Exceptions are raised up the call stack until either they are handled by a layer or reach the root application’s context, at which point they are typically fatal to the calling application, process, or thread (depending on what type of application is running).

Although unhandled exceptions are not fatal to WCF itself, WCF makes the assumption that they indicate a serious issue in the service’s capability to continue communications with the client. In those cases, WCF will fault the service channel, which means any existing sessions (for example, for security, reliable messaging, or state sharing) will be destroyed. If a session is part of the service call, the client channel will no longer be useful, and the client-side proxy will need to be re-created for the client to continue calling the service.

By default, exceptions that reach the service host that are not derived from FaultException are considered indications of a potentially fatal condition. The exception is replaced by FaultException and the original exception’s details are omitted unless the IncludeExceptionDetailInFaults option is enabled. The FaultException is then serialized as a SOAP fault for communication back to the caller.

The fatal condition created by unhandled exceptions can be prevented by catching exceptions before they reach the service host and throwing a FaultException manually.

Most services which require error handling also require additional information to be passed with the error notification. This information can be transferred to the client as a standard WCF data contract, in the disguise of a fault. The contractual specification that a particular service operation can result in the specified fault is called a fault contract.

The WCF service can produce a fault that is part of its fault contract, by throwing an exception. Throwing an exception is the most natural thing to do to indicate failure, for a .NET developer. The service is expected to throw the FaultException<TDetail> generic exception, with TDetail being the actual fault type that is being conveyed to the client. For example, the following service code conveys the ServiceFault fault to the client:

class Service : IService {

public void MyMethod() {

ServiceFault fault = new ServiceFault(...);

throw new FaultException(fault);

}

}

WCF has an excellent built-in extensibility mechanism for converting exceptions to faults. This extensibility point can be consumed through the IErrorHandler interface, which provides two methods: HandleError and ProvideFault. The HandleError method is called on a separate thread after the call has already completed, to possibly log the error and perform other book-keeping operations. The ProvideFault method, on the other hand, is called on the worker thread that is invoking the service call, and accepts the exception that was thrown by the service. It is expected to provide a fault message that will be sent to the client, and thus fits exactly what we are trying to accomplish. At runtime, an implementation of these methods can be hooked up to the ChannelDispatcher on the service side, and automatically get called whenever an unhandled exception escapes the service code.

We will begin with the core of the error handler. Our first attempt could be converting any exception to a FaultException<TDetail> with TDetail as the exception type. For example, if an ArgumentException could be thrown from downstream code, then we convert that to the specific fault. We need a mapping mechanism between .NET exceptions and faults.

In the code attached, I have created class library project that you can use in any of the Service. This Library contains the following classes

IExceptionToFaultConverter is having a method ConvertExceptionToFaultDetail. This method will be responsible for converting any type of Exception to the Fault. You can implement this interface in your service and write any your own logic to convert the exceptions to faults.

ErrorHandler is implementing the IErrorHandler interface and thus implementing HandleError and ProvideFault methods.

ErrorHandlerBehaviourAttribute is used to apply the behavior to your service so that whenever exception is raised, that will be handled in the Channel Dispatcher.

In the Service, we have to implement the IExceptionToFaultConverter method and also specify the behavior to the service.

The behavior can be specified in the following way at the service Level.

[ErrorHandlerBehaviour(

ExceptionToFaultConverter = typeof(ServiceFaultConverter))]

public class Service : IService

{

………….

}

We are here adding the Service behavior and specifying the Converter which we have implemented above.

Summary

The approach outlined in this article allows service developers to focus on their business logic and call downstream facilities directly. It absolves service developers from the need to worry about letting only permitted faults escape the service boundary, and provides a convenient mechanism for mapping .NET exceptions to well-defined WCF faults.
Click here to download source code

No comments:

Post a Comment