Creating a WCF Operation Invoker to handle exceptions

by David Kiff 23. September 2009 15:25

I found myself repeating the same old try..catch logic within our WCF services.  Quite often we would catch specific exceptions and re-throw them as fault exceptions, whereas the rest required logging and re-throwing.

I decided to create a wrapper around the class responsible for invoking the operations.  That will allow us to add a generic try..catch around every operation, that we apply the behaviour to.

The IOperationBehavior behaviour gives us an ApplyDispatchBehavior method that passes the original OperationInvoker, through the DispatchOperation class.  With this we can wrap it and then set it as the OperationInvoker for WCF to use, like so:

internal class ExceptionInterceptorBehaviour : Attribute, IOperationBehavior
{
    public void Validate(OperationDescription operationDescription)
    {
    }

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        IIoC container = new UnityIoC();
        IExceptionOperationInvoker invoker = container.Resolve<IExceptionOperationInvoker>();
        invoker.BaseInvoker = dispatchOperation.Invoker;

        dispatchOperation.Invoker = invoker;
    }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
    {
    }

    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
    {
    }
}

I have used a container to create a new instance of our OperationInvoker wrapper, you can of course just create a new one and pass the dispatchOperation.Invoker into the constructor should you wish.

The wrapper its self is as you would expect:

internal class ExceptionOperationInvoker : IExceptionOperationInvoker
{
    private IOperationInvoker _operationInvoker;
    private readonly ILogger _logger;

    public ExceptionOperationInvoker(ILogManager logManager)
    {
        _logger = logManager.GetLogger(typeof(ExceptionOperationInvoker));
    }

    public IOperationInvoker BaseInvoker
    {
        set
        {
            _operationInvoker = value;
        }
        private get
        {
            if(_operationInvoker == null)
                throw new InvalidOperationException(ExceptionMessages.BASE_INVOKER_NOT_SET_ON_EXCEPTION_INVOKER);

            return _operationInvoker;
        }
    }

    public object[] AllocateInputs()
    {
        return BaseInvoker.AllocateInputs();
    }

    public object Invoke(object instance, object[] inputs, out object[] outputs)
    {
        try
        {
           return BaseInvoker.Invoke(instance, inputs, out outputs);
        }
        catch(NotFoundException ex)
        {
            _logger.Error("NotFoundException", ex);
            throw new FaultException<NotFoundFault>(new NotFoundFault { Message = ex.Message }, ex.Message);
        }
        catch(Exception ex)
        {
            _logger.Error("Exception", ex);
            throw;   
        }
    }

    public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
    {
        return BaseInvoker.InvokeBegin(instance, inputs, callback, state);
    }

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
    {
        return BaseInvoker.InvokeEnd(instance, out outputs, result);
    }

    public bool IsSynchronous { get { return BaseInvoker.IsSynchronous; } }
}

All that is left is to apply the behaviour to the operations that you want.  This can be done within the service contract, by applying it as an attribute:

[ServiceContract]
public interface ICommunicationService

      [OperationContract]
        [ExceptionInterceptorBehaviour] 
      [FaultContract(typeof(DuplicateItemExistsFault))] 
      void MyOperation(); 
}

Tags: , ,

WCF

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading