Dynamic Proxy for WCF with Castle DynamicProxy

In a project I’m currently working we are using Windows Communication Foundation with automatically generated client proxies from interfaces in a shared library. This is the ideal approach for us because we are under control of the service provider and service consumer and when developing our stories for the application we can quickly introduce new service providers which then are automatically available at the client side. So far so good. But when using Windows Communication Foundation the usage of the client proxies tends to get very complicated because the client proxies need to be regenerated when the underlying communication channel is timed out or an exception occurs. So basically we would need to write the following pseudo code for each service request:

using(var client = new ClientProxyOfTypeFoo(someEndpointInformation) /* or proxyFactory.Create() */)
{
   try
   {
      client.Bar();
   }
   catch(CommunicationException coex) { // do something appropriate }
   catch(FaultExceptionOfBar fex) { // do something appropriate }
}

To say the least this approach is:

  • Annoying
  • Cumbersome
  • Complicated with dependency injection
  • Bad for testing when proxy is instantiated directly in code.
  • I’m sure you’ll find more reasons…

Let’s see how we can avoid that with dynamic proxy from castle.

Castle dynamic proxy offers the possibility to add interceptors to a proxy instance. The interceptor needs to implement the IInterceptor interface from castle which has only one method called Intercept. All required information about the proxy being intercepted, the method, the arguments and so on will be passed to the Intercept method. Let’s look into the code.

    /// <summary>
    /// The interceptor which intercepts client proxies.
    /// </summary>
    public class ClientProxyInterceptor : IInterceptor
    {
        private readonly Func<object> creator;

        private object service;

        /// <summary>
        /// Initializes a new instance of the <see cref="ClientProxyInterceptor"/> class.
        /// </summary>
        /// <param name="creator">The creator.</param>
        public ClientProxyInterceptor(Func<object> creator)
        {
            this.creator = creator;

            this.UnwrapFaultExceptions = true;
        }

        /// <summary>
        /// Gets or sets a value indicating whether fault exceptions shall be unwrapped.
        /// </summary>
        /// <value>
        /// <c>true</c> if fault exceptions shall be unwrapped; otherwise, <c>false</c>.
        /// </value>
        public bool UnwrapFaultExceptions { get; set; }

        /// <summary>
        /// Gets or sets the cached proxy.
        /// </summary>
        /// <value>The cached proxy.</value>
        public object CachedProxy
        {
            get
            {
                this.EnsureProxyExists();
                return this.service;
            }

            set
            {
                this.service = value;
            }
        }

        /// <summary>
        /// Intercepts the specified invocation.
        /// </summary>
        /// <param name="invocation">The invocation.</param>
        public void Intercept(IInvocation invocation)
        {
            Action request = () => { };
            try
            {
                request =
                    () => invocation.ReturnValue = invocation.Method.Invoke(this.CachedProxy, invocation.Arguments);

                request();
            }
            catch (TargetInvocationException ex)
            {
                Exception innerException = ex.InnerException;

                this.CloseProxyBecauseOfException();

                var fault = innerException as FaultException;

                if (fault != null)
                {
                    if (this.UnwrapFaultExceptions && IsGenericFaultException(fault.GetType()))
                    {
                        dynamic genericFault = fault;
                        Exception detailException = genericFault.Detail;
                        throw detailException;
                    }

                    throw fault;
                }

                if (innerException is CommunicationException)
                {
                    try
                    {
                        request();
                        return;
                    }
                    catch (TargetInvocationException)
                    {
                        throw innerException;
                    }
                }

                throw innerException;
            }
        }

        /// <summary>
        /// Determines whether the specified fault exception is a generic fault
        /// exception.
        /// </summary>
        /// <param name="faultType">The fault exception type.</param>
        /// <returns>
        /// <c>true</c> if the specified fault exception is a generic fault
        /// exception; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsGenericFaultException(Type faultType)
        {
            return faultType.IsGenericType &&
                typeof(FaultException<>).IsAssignableFrom(faultType.GetGenericTypeDefinition());
        }

        /// <summary>
        /// Ensures the existance of the proxy.
        /// </summary>
        private void EnsureProxyExists()
        {
            if (this.service == null)
            {
                this.service = this.creator();
            }
        }

        /// <summary>
        /// Close the proxy because it was fauled.
        /// </summary>
        private void CloseProxyBecauseOfException()
        {
            if (this.CachedProxy != null)
            {
                var wcfProxy = this.CachedProxy as ICommunicationObject;
                try
                {
                    if (wcfProxy != null)
                    {
                        if (wcfProxy.State != CommunicationState.Faulted)
                        {
                            wcfProxy.Close();
                        }
                        else
                        {
                            wcfProxy.Abort();
                        }
                    }
                }
                catch (CommunicationException)
                {
                    if (wcfProxy != null)
                    {
                        wcfProxy.Abort();
                    }
                }
                catch (TimeoutException)
                {
                    if (wcfProxy != null)
                    {
                        wcfProxy.Abort();
                    }
                }
                catch
                {
                    if (wcfProxy != null)
                    {
                        wcfProxy.Abort();
                    }

                    throw;
                }
                finally
                {
                    this.CachedProxy = null;
                }
            }
        }
    }

The idea of the client interceptor is to give the interceptor knowledge about how to create an instance of the channel proxy which holds the underlying WCF communication means. This is done by passing a function delegate as constructor argument to the interceptor. So every time the underlying communication channel is faulted the interceptor can recreate an instance of the channel. The property CachedProxy ensures that when the property is accessed and no proxy exists a new instance is created automatically.

The heart of the intercept method declares an action delegate which takes the method information and arguments from the actual call and reroutes it to the CachedProxy instance. The request is then executed and when a TargetInvocationException occurs the exception information is analyzed and actions are taken depending on the inner exception type of the TargetInvocationException. But first the underlying CachedProxy is correctly closed and deferenced (see CloseProxyBecauseOfException) according to the WCF guidelines.

Because I wanted to enable the possibility to unwrap fault exceptions when they are generic fault exception types I have a special check for fault exception types (see lines 67-79). Let’s review the trickery I did there 😉 When the UnwrapFaultExceptions property is set to true the exception type is analyzed whether it is assignable to the open generic type FaultException<>. If this is the case I can safely assume that there is a property with name Detail which contains the wrapped exception which was actually thrown at the service publisher side. To avoid complex reflection (or even some sort of dynamic type cast) I used the dynamic keyword to call the Detail property (for me one of the good reasons to use dynamic keyword).

If the inner exception is a communication exception the request method is invoked a second time and the cleaned proxy because often there is the case that when the client does not communicate for a certain amount of time (default 60 seconds) the first call to the service throws a communication exception and the second call to a recreated channel proxy will succeed because the underlying communication channel is not faulted anymore. Of course when the server is offline this means for the worst case I will be calling the server a second time which is actually useless. But I can live with that (call me pragmatic…). So far that’s the interceptor. But how are the instances created? See the code below…

	using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.ServiceModel;
    using System.ServiceModel.Channels;

    using Castle.Core.Interceptor;
    using Castle.DynamicProxy;

public class ProxyCreationHelper
    {
private const string ProxyCreatorMethodName = "CreateChannel";
        private readonly ProxyGenerator proxyGenerator;

        /// <summary>
        /// Initializes a new instance of the <see cref="ProxyCreationHelper"/> class.
        /// </summary>
        public ProxyCreationHelper()
        {
            this.proxyGenerator = new ProxyGenerator();
        }

        public TInterface Create<TInterface>()
        {
			var interfaceTypeToProxy = typeof(TInterface);
			var proxyCreatorType = this.MakeGenericType(interfaceTypeToProxy);
			var proxyCreator = this.GetProxyCreator(proxyCreatorType);
			var binding = this.CreateBinding();
			var address = this.CreateEndpointAddress(interfaceTypeToProxy);
			var endpointAddress = new EndpointAddress(address);

			Type type = facadeType;
			return this.proxyGenerator
				.CreateInterfaceProxyWithoutTarget(type, new[] { typeof(IContextChannel), }, this.CreateInterceptor(proxyCreator, binding, endpointAddress));
        }

		/// <summary>
        /// Delegate which is used to bind against the proxy creator method
        /// found on the <see cref="ChannelFactory"/>.
        /// </summary>
        /// <param name="binding">The binding to be passed to the proxy creator.</param>
        /// <param name="endpointAddress">The endpoint address to be passed to the proxy creator.</param>
        /// <returns>The created service facade proxy.</returns>
        protected delegate IServiceFacade ProxyInstanceCreator(Binding binding, EndpointAddress endpointAddress);

		/// <summary>
        /// Creates the standard binding
        /// </summary>
        /// <returns>The standard binding.</returns>
        protected virtual Binding CreateBinding()
        {
            // Currently disabled security.
            return new NetTcpBinding(SecurityMode.None);
        }

        /// <summary>
        /// Creates the endpoint address naming convention.
        /// </summary>
        /// <param name="interfaceTypeToProxy">The interface type to proxy.</param>
        /// <returns>The combined endpoint address.</returns>
        protected virtual string CreateEndpointAddress(Type interfaceTypeToProxy)
        {
            string typeName = interfaceTypeToProxy.Name;

            if (interfaceTypeToProxy.IsInterface)
            {
                typeName = typeName.Substring(1);
            }

            return string.Format(CultureInfo.InvariantCulture, "net.tcp://{0}/{1}", this.EndpointAddress, typeName);
        }

		/// <summary>
        /// Creates the standard interceptor.
        /// </summary>
        /// <param name="proxyCreator">The proxy creator.</param>
        /// <param name="binding">The binding.</param>
        /// <param name="endpointAddress">The endpoint address.</param>
        /// <returns>A new instance of the standard interceptor.</returns>
        protected virtual IInterceptor CreateInterceptor(ProxyInstanceCreator proxyCreator, Binding binding, EndpointAddress endpointAddress)
        {
            return new ClientProxyInterceptor(() => proxyCreator(binding, endpointAddress));
        }

        /// <summary>
        /// Extracts the proxy creator delegate out of the generic proxy creator type.
        /// </summary>
        /// <param name="genericProxyCreatorType">The generic proxy creator type to be used.</param>
        /// <returns>The proxy creator.</returns>
        private ProxyInstanceCreator GetProxyCreator(Type genericProxyCreatorType)
        {
            ProxyInstanceCreator proxyInstanceCreator;

			MethodInfo method = genericProxyCreatorType.GetMethod(
				ProxyCreatorMethodName, new[] { typeof(Binding), typeof(EndpointAddress) });
			proxyInstanceCreator =
				(ProxyInstanceCreator)Delegate.CreateDelegate(typeof(ProxyInstanceCreator), method);

            return proxyInstanceCreator;
        }

        /// <summary>
        /// Creates a generic type of the WcfClientProxy.
        /// </summary>
        /// <param name="interfaceTypeToProxy">The interface type to proxy.</param>
        /// <returns>The generic type.</returns>
        private Type MakeGenericType(Type interfaceTypeToProxy)
        {
            if (!interfaceTypeToProxy.IsInterface)
            {
                throw new InvalidOperationException(
                    "Type returned from proxy type provider must not be concrete type!");
            }

            Type genericProxyType;

			genericProxyType = typeof(ChannelFactory<>).MakeGenericType(new[] { interfaceTypeToProxy });

            return genericProxyType;
        }
	}

WCF client proxies are generated in code by using the ChannelFactory. There is a generic ChannelFactory which as several CreateChannel method overloads which take parameters to instantiate the channel proxy. In this example the ProxyCreationHelper reflects on the CreateChannel overload which allows to define a binding and an endpoint as parameter. This is done with MakeGenericType and GetProxyCreator (which turns the method info of the CreateChannel into a typed delegate). After having the proxy creation delegate, which is later passed to the interceptor, we only need to create a binding and an endpoint address. Then a proxy which contains the channel proxy is created with CreateInterfaceProxyWithoutTarget and a new client interceptor which has access to the proxy creation delegate, the binding and the endpoint. The proxy also implements IContextChannel so that it is possible to set the timeout of the channel in code. That’s it!

How would the call look like to the above FooService?

Bar();
[--- FooServiceDynamicProxy
[------- ClientProxyInterceptor.Intercept(...)
[------- // recreate cached proxy if necessary
[------- try
[---------- CachedProxy.Bar();
[------- catch
[---------- // when fault exception, unwrap if necessary
[---------- // when communication exception
[---------- CachedProxy.Bar();
// return to caller

You wonder how the test code for the interceptor might look like? This might be a story for another blog post 😉 Have fun.

About the author

Daniel Marbach

9 comments

  • Thank you Daniel. You helped me a lot.
    What is ‘facadeType’ in line 33, ProxyCreationHelper class?
    What is a trick you use in the following code?
    request = () => invocation.ReturnValue = invocation.Method.Invoke(this.CachedProxy, invocation.Arguments);
    request();
    Why not just ‘invocation.ReturnValue = invocation.Method.Invoke(this.CachedProxy, invocation.Arguments)’?
    Thank you.

  • Hy Dennis,
    Line 33 should be typeof(TInterface)
    The delegate trick I used was to simply be able to call request again when it’s needed. If you look in ClientProxyInterceptor line 85 you see that I do a second request when the proxy ran into a timeout.

    I’m plan to work on another version of the interceptor. The version I show above has the following disadvantages:

    (1) What I did with the fault exception is wrong. I just did not understand them enough when I was building it.
    (2) Performance:
    a) Handling the timeout case with exceptions is not very elegant and it is time consuming.
    b) The life time of the channel is too long which contradicts microsofts guidelines with client proxies.

    The next version I’m planning will handle these scenarios correctly.

    Daniel

  • Ah, I missed the second use of the ‘request’.
    I borrowed your idea with intercepted WCF client proxy to log method call arguments. Thank you very much.

    My next task is to implement exception handling.
    So thanks for your caution.

    Dennis

  • Hey I am a newbie with Mocks. The scenario I have is a Third Party WSDL which I managed to generate a Proxy Client and then used Reflection to extract Method,Parameters, Properties and Fields but I lost nerves when I tried to invoke methods with Parameters.
    Then came across a DynamicProxyClient looked good suddenly I saw your article. Remember my Scenario without disturbing your routine see if you can help me witht he missing parts.
    Thanks in Advance

  • Hi Daniel
    I was also thinking about a similar functionality on the lines and I came across this link thanks
    Ok the exceptions part ,well I also came across a Pattern called Circuit Breaker that can be applied to Services(Http especially) like a state machine states – Open ,Half Open and Closed
    depending on the state we can take decision
    I think I will make a hybrid of your interceptor and Circuit Breaker pattern and will try to make it work
    thanks
    rama anne

  • Hi daniel
    Open state – Failure it gdid not get through(Proxy connection)
    Half Open – Retries
    Closed – Sucess Proxy makes connection(Channel good (not faulty )
    rama anne

By Daniel Marbach

Recent Posts