Support us .Net Basics C# SQL ASP.NET ADO.NET MVC Slides C# Programs Subscribe Buy DVD

Part 20 - Centralized exception handling in WCF by implementing IErrorHandler interface

Suggested Videos
Part 17 - Unhandled exceptions in WCF
Part 18 - Throwing fault exceptions from a WCF service
Part 19 - Creating and throwing strongly typed SOAP faults



This is continuation to Part 19, please watch Part 19 before proceeding.

In this video, we will discuss - How to handle all WCF service exceptions in one central location. This is a very common interview question.

In an ASP .NET web applications we can use Application_Error() event handler method in Global.asax to log all the exceptions and redirect the user to a custom error page. 

In WCF, to centralize exception handling and to return a general faultreason to the client, we implement IErrorHandler interface. Let's now look at the 3 steps involved in centralizing exception handling in WCF. We will be continuing with the same example, that we worked with in Part 19.



Step 1: Implement IErrorHandler interface. To do this, right click on CalculatorService project and add a class file with name = GlobalErrorHandler.cs. Copy and paste the following code. 
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
namespace CalculatorService
{
    public class GlobalErrorHandler : IErrorHandler
    {
        public bool HandleError(Exception error)
        {
            // log the actual exception for the IT Team to investigate
            // return true to indicate that the exception is handled
            return true;
        }

        public void ProvideFault(Exception error, 
                                 System.ServiceModel.Channels.MessageVersion version, 
                                 ref System.ServiceModel.Channels.Message fault)
        {
            if (error is FaultException)
                return;

            // Return a general service error message to the client
            FaultException faultException = new FaultException("A general service error occured");
            MessageFault messageFault = faultException.CreateMessageFault();
            fault = Message.CreateMessage(version, messageFault, null);
        }
    }
}

IErrorHandler interface has 2 methods for which we need to provide implementation.
1. ProvideFault() - This method gets called automatically when there is an unhandled exception or a fault. In this method we have the opportunity to write code to convert the unhandled exception into a generic fault that can be returned to the client. ProvideFault() gets called before HandleError() method.

2. HandleError() - This method gets called asynchronously after ProvideFault() method is called and the error message is returned to the client. This means that this method allows us to write code to log the exception without blocking the client call.

Step 2: Create a custom Service Behaviour Attribute to let WCF know that we want to use the GlobalErrorHandler class whenever an unhandled exception occurs. To do this, right click on the CalculatorService project and add a class file with name =  GlobalErrorHandlerBehaviourAttribute.cs. Copy and paste the following code. 
using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace CalculatorService
{
    public class GlobalErrorHandlerBehaviourAttribute : Attribute, IServiceBehavior
    {
        private readonly Type errorHandlerType;

        public GlobalErrorHandlerBehaviourAttribute(Type errorHandlerType)
        {
            this.errorHandlerType = errorHandlerType;
        }

        public void Validate(ServiceDescription serviceDescription,
                             ServiceHostBase serviceHostBase)
        {
        }

        public void AddBindingParameters(ServiceDescription serviceDescription,
                                         ServiceHostBase serviceHostBase,
                                         Collection<ServiceEndpoint> endpoints,
                                         BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
                                          ServiceHostBase serviceHostBase)
        {
            IErrorHandler handler = (IErrorHandler)Activator.CreateInstance(this.errorHandlerType);

            foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                if (channelDispatcher != null)
                    channelDispatcher.ErrorHandlers.Add(handler);
            }
        }
    }
}

Notice that the GlobalErrorHandlerBehaviourAttribute class
1. Inherits from Attribute abstract class.

2. Implements IServiceBehavior interface. This interface has 3 methods (Validate(), AddBindingParameters(), ApplyDispatchBehavior()). The implementation for Validate() and AddBindingParameters() method can be left blank. In the ApplyDispatchBehavior() method, we create an instance of the GlobalErrorHandler class and associate the instance with each channelDispatcher. 

3. Has a constructor that contains one Type parameter. We will use this constructor in Step 3.

Step 3: Decorate CalculatorService class in CalculatorService.cs file with GlobalErrorHandlerBehaviourAttribute. Notice that this attribute has one constructor that expects a single Type parameter. Pass GlobalErrorHandler class created in Step 1 as the argument.
[GlobalErrorHandlerBehaviour(typeof(GlobalErrorHandler))]
public class CalculatorService : ICalculatorService
{
    public int Divide(int Numerator, int Denominator)
    {
    }
}

For testing the global centralized error handler
1. Comment try-catch blocks in the Divide() method in CalculatorService.cs file
public int Divide(int Numerator, int Denominator)
{
    //try
    //{
        return Numerator / Denominator;
    //}
    //catch (DivideByZeroException ex)
    //{
    //    DivideByZeroFault divideByZeroFault = new DivideByZeroFault();
    //    divideByZeroFault.Error = ex.Message;
    //    divideByZeroFault.Details = "Denominator cannot be ZERO";

    //    throw new FaultException<DivideByZeroFault>(divideByZeroFault);
    //}
}

2. Put a break point in ProvideFault() and HandleError() methods

3. Run the service in Debug mode

4. In the client application change the catch in button1_Click() event handler as shown below.
catch (FaultException faultException)
{
    label1.Text = faultException.Message;

}

5. Run the client application. Pass 0 as the denominator and click Divide button. 

Notice that ProvideFault() and HandleError() methods in GlobalErrorHandler class are automatically called.

wcf tutorial

2 comments:

  1. sir can u create tutorial for crossplatform android,ios,windows visual studio 2015

    ReplyDelete
  2. Hi All,

    I have tried the same Code, but while handling error 'Provide fault' method is not even been hit. Can anyone please let me know the places where I could have gone wrong??? Please advise.

    I have added the below attribute on 'Calculator' class aswell.
    [GlobalErroHandlerBehaviour(typeof(GlobalErrorHandler))]
    public int Divide(int numerator, int denominator)

    ReplyDelete

If you like this website, please share with your friends on facebook and Google+ and recommend us on google using the g+1 button on the top right hand corner.