WCF Exception Handling with Exception Handling Application Block Integration

This post is one of a post series about WCF Integration in Enterprise Library 3.0. Specifically, this post is the first post I am talking about the integration between Exception Handling Application Block and Windows Communication Foundation (WCF).
This post has a brief overview about the Exception Handling Application
Block (EAB) and the WCF Fault Handling. Then, this post explains how to
use the EAB integration with WCF.

Exception Handling Application Block

If you’re familiar with the Exception Handling Application Block,
you can skip to the next section. If you’re not, stay around. To simply
put it, the EAB works with exception handling policies. You configure a
exception policy in the configuration file, and add one or more
exception handlers to it. For example, the following configuration
snippet configures a policy called "Server Policy"
that replaces a Security Exceptions with Application Exceptions, and
wraps a DB Concurrency Exception with a custom Exception Type.

<exceptionPolicies>

  <add name="Server Policy">

    <exceptionTypes>

      <add name="SecurityException"

           type="System.Security.SecurityException 

           postHandlingAction="ThrowNewException">

        <exceptionHandlers>

          <add name="Replace Handler"

               type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler"

               exceptionMessage="Replaced: User is not authorized to perform the requested action."

               replaceExceptionType="System.ApplicationException, mscorlib" />

        </exceptionHandlers>

      </add>

      <add name="DBConcurrencyException"

           type="System.Data.DBConcurrencyException, System.Data"

           postHandlingAction="ThrowNewException">

        <exceptionHandlers>

          <add name="Wrap Handler"

               type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WrapHandler"

               exceptionMessage="Wrapped Exception: An error occurred while attempting to access the DB."

               wrapExceptionType="MyApplicationException, MyApplication.Excecptions" />

        </exceptionHandlers>

      </add>

    </exceptionTypes>

  </add>

</exceptionPolicies>

Then, on order to handle an exception when caught, we use the following code:

try

{

    // Do some operation that may cause an exception

}

catch

{

    // Use the EAB to handle the exceptions according to the configuration

    if ( ExceptionPolicy.HandleException(ex, "Server Policy") ) throw;

}

WCF and Exceptions

WCF Exception Handling
These is a lot to say about what happens when an exception occurs in
a WCF Service Operation. To make a long story short, I’ll say that the
exception is not thrown back to the client and the channel becomes in Faulted
state. The idea behind this design is that you don’t want to reveal any
information about the error that has occurs because it can contain
information you don’t want to share. For example, if you expose a Web
Service to the Internet for other organizations to consume, you don’t
want them to know that you cannot insert a new record to table "tblUsers" because a user with name "Guy" already exists.

If
you still want to return excecptions back to the client (recommended
only during development time), you can add a service behavior: 

<serviceBehaviors>

  <behavior name="Debug">

    <serviceDebug includeExceptionDetailInFaults="true" />

  </behavior>

</serviceBehaviors>

Note that this configuration is used only for unhandeled exceptions. If you are using a FaultContract to specify what type of faults your service might return, this setting will not change a thing about it.

Exception Handling Application Block Integration with WCF

In
this post, I will not get into details about how the integration works,
and I will focus on the things you should do or configure in order to
benefit from this integration.

First, you have to create a Data Contract that will contain the details about the error that has occurred. For example:

[DataContract]

public class ServiceFault

{

    private string message;

    private Guid id;

 

    [DataMember]

    public string MessageText

    {

        get { return message; }

        set { message = value; }

    }

 

    [DataMember]

    public Guid Id

    {

        get { return id; }

        set { id = value; }

    }

}

Second,
you have to create a WCF shielding policy in the exception handling
policies section in the configuration file, as displayed in this
configuration snippet.

<exceptionHandling>

  <exceptionPolicies>

    <add name="WCF Exception Shielding">

      <exceptionTypes>

        <add type="System.InvalidOperationException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="ThrowNewException" name="InvalidOperation">

          <exceptionHandlers>

            <add           type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler,
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF
"

              name="DefaultFaultContract Handler"

              faultContractType="Bursteg.Samples.WCFIntegration.ServiceContracts.ServiceFault, Bursteg.Samples.WCFIntegration.ServiceContracts">

              <mappings>

                <add name="Id" source="{Guid}"/>

                <add name="MessageText" source="{Message}"/>

              </mappings>

            </add>

          </exceptionHandlers>

        </add>

      </exceptionTypes>

    </add>

  </exceptionPolicies>

</exceptionHandling>

There are few things that you should notice in the above snippet:

1. The default name of the exception handling policy is WCF Exception Shielding. The exception handling app. block will look for this name if you don’t tell it otherwise (more details later).

2. Make sure that the PostHandlingAction of the policy is ThrowNewException. This means that the exception with the FaultContract in it will be thrown after the handling is done.

3. The Exception handler called DefaultFaultContract  Handler is an Exception Handler of type FaultContractExceptionHandler (more details on the next post) which converts the handled exception (this time it’s System.InvalidOperationException) to the FaultContract specified in the faultContractType attribute.

4.
What this handler does in order to convert an exception to the
configured fault contact, is simply mapping of properties according to
the <mappings> list above. In this case the property MessageText in the ServiceFault class will get the value in the Message property of the exception. Notice that the Id property of the ServiceFault is mapped to the source {Guid}.
This means that the Id property will have the Handling Instance Id
property of the exceptions that is automatically created by the
Exception Handling Application Block.

Third, you have to add the exception shielding into the service contract or the service class. This is done by the [ExceptionShielding] attribute.

[ServiceContract]

[ExceptionShielding]

public interface IOrdersService

{

    …

}

If you changed the name of the policy that the exception shielding is using from WCF Exception Shielding, or you have many services and each one has its own specific policy, you should pass the name of the policy to this attribute:

[ServiceContract]

[ExceptionShielding("Policy Name")]

public interface IOrdersService

{

    …

}

As I mentioned earlier, the FaultContractExceptionHandler
converts the exception to a Fault Contract. This means that you have to
specify the FaultContract to your service contract in order for
receiving it in the client side:

[ServiceContract]

[ExceptionShielding]

public interface IOrdersService

{

    [OperationContract]

    [FaultContract(typeof(ServiceFault))]

    int CreateOrder(string currency, double amount);

 }

One thing you should note is that if you used the <serviceDebug> option and set the includeExceptionDetailInFault to true, the exception shielding will not work. So be sure to turn this option off.

Now, in order to receive this meaningful exception in the client, you should catch a FaultException<T> where T is the FaultContract use used.

try

{

    // Call the service method that has a fault contract on it

}

catch (FaultException<ServiceFault> ex)

{

    ServiceFault fault = ex.Detail;

    Console.WriteLine(fault.MessageText);

    Console.WriteLine(fault.Id);

}

You can download a sample project that shows this integration.

This entry was posted in Best Practices. Bookmark the permalink.

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s