IOperationInvoker–How to wrap each call in the server side?

Posted on December 15, 2010 by

0


IOperationInvoker is responsible for invoking the methods on the server side with the required parameters and generate the required output parameters and the method’s return value.

Its interface contains the following methods:

 

 public interface IOperationInvoker { object[] AllocateInputs();
     object Invoke(object instance, object[] inputs, out object[] outputs);
     System.IAsyncResult InvokeBegin(object instance, object[] inputs, System.AsyncCallback callback, object state);
     object InvokeEnd(object instance, out object[] outputs, System.IAsyncResult result);
     bool IsSynchronous { get; }
 }

Creating a custom  IOperationInvoker allows to effectively wrap each call to an operation in a try-catch blocks, where you can call the required method and handle exceptions with your own logic.

This extension can be used only in the server side and it is added through the System.ServiceModel.DispatchOperation with the Invoker property in the following way:

    internal sealed class MyOperationInvoker : IOperationBehavior
    {
        private IEnumerable<Type> m_fatalExceptions;

        public MyOperationInvoker(IEnumerable<Type> fatalExceptions)
        {
            m_fatalExceptions = fatalExceptions;
        }

        #region IOperationBehavior Members

        ...

        public void ApplyDispatchBehavior(OperationDescription operationDescription,
            DispatchOperation dispatchOperation)
        {
            dispatchOperation.Invoker = new MyOperationInvoker(
                m_fatalExceptions,
                dispatchOperation.Invoker);
        }

        #endregion
    }

IOperationInvoker Sample

In the following sample MyOperationInvoker receives a list of fatal exceptions in its constructor, and when one of these exceptions is received after invoking the server’s  method, it crashes the server application, while all other exceptions are swallowed.


    internal class MyOperationInvoker : IOperationInvoker
    {
        private IEnumerable<Type> m_fatalExceptions;
        private IOperationInvoker m_innerOperationInvoker;

        public MyOperationInvoker(IEnumerable<Type> fatalExceptions,
                                  IOperationInvoker invoker)
        {
            m_fatalExceptions = fatalExceptions;
            m_innerOperationInvoker = invoker;
        }

        #region IOperationInvoker Members

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

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            try
            {
                return this.m_innerOperationInvoker.Invoke(
                        instance, inputs, out outputs);
            }
            catch (Exception e)
            {
                FailServerOnFatalExceptions(e);
                throw;
            }
        }

        public IAsyncResult InvokeBegin(object instance, object[] inputs,
            AsyncCallback callback, object state)
        {
            try
            {
                return this.m_innerOperationInvoker.InvokeBegin(
                    instance, inputs, callback, state);
            }
            catch (Exception e)
            {
                FailServerOnFatalExceptions(e);
                throw;
            }
        }

        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
        {
            try
            {
                return this.m_innerOperationInvoker.InvokeEnd(
                    instance, out outputs, result);
            }
            catch (Exception e)
            {
                FailServerOnFatalExceptions(e);
                throw;
            }
        }

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

        #endregion

        #region private methods

        private bool ExceptionIsInFatalExceptionList(Type operationExceptionType)
        {
            foreach (var fatalException in m_fatalExceptions)
            {
                if (ExceptionTypesMatch(operationExceptionType, fatalException))
                {
                    return true;
                }
            }
            return false;
        }

        private bool ExceptionTypesMatch(Type operationExceptionType, Type fatalExceptionType)
        {
            return (
                fatalExceptionType.Equals(operationExceptionType) ||
                operationExceptionType.IsSubclassOf(fatalExceptionType)
            );
        }

        private void FailServerOnFatalExceptions(Exception error)
        {
            if (ExceptionIsInFatalExceptionList(error.GetType())
            {
                Environment.FailFast(error.Message ?? "Crash handler threw a fatal exception");
            }
        }

        #endregion

    }    

Advertisements
Posted in: WCF Extensions