Section 6.1. Errors and Exceptions


6.1. Errors and Exceptions

In raw .NET programming, any unhandled exception immediately terminates the process it took place in. That is not the WCF behavior, however. If a service call on behalf of one client causes an exception, it must not be allowed to take down the hosting process. Other clients accessing the service, or other services hosted by the same process, should not be affected. As a result, when an unhandled exception leaves the service scope, the dispatcher silently catches and handles it by serializing it in the returned message to the client. When the returned message reaches the proxy, the proxy throws an exception on the client side.

The client can actually encounter three types of errors when trying to invoke the service. The first type of error is communication errors such as network availability, wrong address, host process not running, and so on. Communication exceptions are manifested on the client side by the CommunicationException.

The second type of error the client might encounter is related to the state of the proxy and the channels, such as trying to access an already closed proxy, resulting in an ObjectDisposedException, or a mismatch in the contract and the binding security protection level.

The third type of error is the one that originated in the service call, either by the service throwing an exception or as a result of the service calling another object or resource and having that internal call throw an exception. These errors are the subject of this chapter.

In the interest of encapsulation and decoupling, by default all exceptions thrown on the service side always reach the client as FaultException:

 public class FaultException : CommunicationException {...} 

By having all service exceptions indistinguishable from each other, WCF decouples the client from the service. The less the client knows about what happened on the service side, the more decoupled the interaction will be.

6.1.1. Exceptions and Instance Management

While WCF does not take down the host process when the service instance encounters an exception, the error may affect the service instance and the ability of the client to continue using the proxy (or actually the channel) to the service. The exact effect the exception has on the client and the service instance depends on the instance mode.

6.1.1.1. Per-call service and exceptions

If the call encounters an exception, after the exception the service instance is disposed and the proxy throws a FaultException on the client's side. By default, all service-thrown exceptions (with the exception of FaultException-derived classes) fault the channel so that even if the client catches that exception, it cannot issue subsequent calls because those yield a CommunicationObjectFaultedException. The client can only close the proxy.

6.1.1.2. Sessionful service and exceptions

When you use any of the WCF sessionful bindings, by default all exceptions (with the exception of FaultException-derived classes) terminate the session. WCF disposes of the instance and the client gets a FaultException. Even if the client catches that exception, it cannot continue using the proxy because subsequent calls yield a CommunicationObjectFaultedException. The only thing the client can safely do is to close the proxy, because once the service instance participating in the session encounters an error, the session should no longer be used.

6.1.1.3. Singleton service and exceptions

When you call a singleton service and encounter an exception, the singleton instance is not terminated and continues running. By default, all exceptions (with the exception of FaultException-derived classes) fault the channel and the client cannot issue subsequent calls other than closing the proxy. If the client had a session with the singleton, that session is terminated.

6.1.2. Faults

The fundamental problem with exceptions is that they are technology-specific and as such should not be shared across the service boundary. For seamless interoperability, you need a way to map technology-specific exceptions to some neutral error information. This representation is called soap faults. Soap faults are based on an industry standard that is independent of any technology-specific exceptions such as CLR, Java, or C++ exceptions. To throw a soap fault (or just a fault for short) the service cannot throw a raw CLR exception. Instead, the service should throw an instance of the FaultException<T> class, defined in Example 6-1.

Example 6-1. The FaultException<T> class

 [Serializable] //More attributes public class FaultException : CommunicationException {    public FaultException( );    public FaultException(string reason);    public FaultException(FaultReason reason);    public virtual MessageFault CreateMessageFault( );    //More members } [Serializable] public class FaultException<T> : FaultException {    public FaultException(T detail);    public FaultException(T detail,string reason);    public FaultException(T detail,FaultReason reason);    //More members } 

FaultException<T> is a specialization of FaultException, so any client that programs against FaultException will be able to handle FaultException<T> as well.

The type parameter T for FaultException<T> conveys the error details. The detailing type can be any type, not necessarily an Exception-derived class. The only constraint is that the type must be serializable or a data contract.

Example 6-2 demonstrates a simple calculator service that throws a FaultException<DivideByZeroException> in its implementation of the Divide( ) operation when asked to divide by zero.

Example 6-2. Throwing a FaultException<T>

 [ServiceContract] interface ICalculator {    [OperationContract]    double Divide(double number1,double number2);    //More methods } class Calculator : ICalculator {    public double Divide(double number1,double number2)    {       if(number2 == 0)       {          DivideByZeroException exception = new DivideByZeroException( );          throw new FaultException<DivideByZeroException>(exception);       }       return number1 / number2;    }    //Rest of the implementation } 

Instead of FaultException<DivideByZeroException> the service could have also thrown a non-Exception-derived class:

 throw new FaultException<double>( ); 

However, I find that using an Exception-derived detailing type is more aligned with the conventional .NET programming practices, and results in more readable code. In addition, it allows for exception promotion, discussed later on.

The reason parameter passed to the constructor of FaultException<T> is used as the exception message. You can pass a mere string for the reason:

 DivideByZeroException exception = new DivideByZeroException( ); throw new FaultException<DivideByZeroException>(exception,"number2 is 0"); 

or you can pass a FaultReason, which is useful when localization is required.




Programming WCF Services
Programming WCF Services
ISBN: 0596526997
EAN: 2147483647
Year: 2004
Pages: 148
Authors: Juval Lowy

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net