Dynamic Proxy for WCF with Castle DynamicProxy Reloaded

This is a follow up post from the post Dynamic Proxy for WCF with Castle DynamicProxy. I updated the code in the last post. I just want to share here a better implementation without much words. The new implemenation uses only the ICommunicationObject interface. The service is correctly closed when not needed anymore. For instantiating and binding the WCF client proxies on the ninject kernel a ToMethod binding is used which calls into the proxyGenerator. But see for yourself.

   public class ClientProxyInterceptor : IInterceptor
    {
        private readonly Func<ICommunicationObject> proxyCreator;
        private readonly Type typeToProxy;
        private ICommunicationObject proxyInstance;

        public ClientProxyInterceptor(Func<ICommunicationObject> proxyCreator, Type typeToProxy)
        {
            this.typeToProxy = typeToProxy;
            this.proxyCreator = proxyCreator;
        }

        public ICommunicationObject CachedProxy
        {
            get
            {
                this.EnsureProxyExists();
                return this.proxyInstance;
            }
            set
            {
                this.proxyInstance = value;
            }
        }

        public void Intercept(IInvocation invocation)
        {
            try
            {
                invocation.ReturnValue = invocation.Method.Invoke(this.CachedProxy, invocation.Arguments);
            }
            catch (TargetInvocationException ex)
            {
                Exception innerException = ex.InnerException;

                throw innerException;
            }
            finally
            {
                this.CloseProxy(invocation.Method);
            }
        }

        private void EnsureProxyExists()
        {
            if (this.proxyInstance == null)
            {
                this.proxyInstance = this.proxyCreator();
            }
        }

        private void CloseProxy(MethodInfo methodInfo)
        {
            var wcfProxy = this.CachedProxy;

            if (wcfProxy != null && this.typeToProxy.IsAssignableFrom(methodInfo.DeclaringType))
            {
                if (wcfProxy.State == CommunicationState.Faulted)
                {
                    this.AbortCommunicationObject(wcfProxy);
                }
                else if (wcfProxy.State != CommunicationState.Closed)
                {
                    try
                    {
                        wcfProxy.Close();

                        this.CachedProxy = null;
                    }
                    catch (CommunicationException)
                    {
                        this.AbortCommunicationObject(wcfProxy);
                    }
                    catch (TimeoutException)
                    {
                        this.AbortCommunicationObject(wcfProxy);
                    }
                    catch (Exception)
                    {
                        this.AbortCommunicationObject(wcfProxy);
                        throw;
                    }
                }
            }
        }

        private void AbortCommunicationObject(ICommunicationObject wcfProxy)
        {
            wcfProxy.Abort();

            this.CachedProxy = null;
        }
    }

Here goes the initializer (or at least part of it).

    public class ServicesInitializer
    {
        public override void Start()
        {
            foreach (Type serviceType in this.ServiceTypeProvider.Types)
            {
                var binding = this.CreateBinding();
                var address = this.CreateEndpointAddress(serviceType);
                var endpointAddress = new EndpointAddress(address);

                var proxyCreatorType = this.MakeGenericType(serviceType);
                var proxyCreator = this.GetProxyCreator(proxyCreatorType, binding, endpointAddress);

                Type type = serviceType;
                this.kernel.Bind(serviceType).ToMethod(
                    ctx => this.proxyGenerator.CreateInterfaceProxyWithoutTarget(type, new[] { typeof(IContextChannel), }, this.CreateInterceptor(proxyCreator, type)));
            }
        }

        public override void Shutdown()
        {
            foreach (IChannelFactory channelFactory in this.proxyInstanceCreatorCache.Values)
            {
                channelFactory.Close();
            }
        }

        protected virtual IInterceptor CreateInterceptor(IChannelFactory proxyCreator, Type serviceType)
        {
            dynamic channelFactory = proxyCreator;
            return new ClientProxyInterceptor(() => (ICommunicationObject)channelFactory.CreateChannel(), serviceType);
        }

        protected virtual IChannelFactory CreateProxyInstanceCreator(Type genericProxyCreatorType, Binding binding, EndpointAddress endpointAddress)
        {
            IChannelFactory proxyInstanceCreator = (IChannelFactory)Activator.CreateInstance(genericProxyCreatorType, binding, endpointAddress);
            return proxyInstanceCreator;
        }

        private IChannelFactory GetProxyCreator(Type genericProxyCreatorType, Binding binding, EndpointAddress endpointAddress)
        {
            IChannelFactory proxyInstanceCreator;
            if (!this.proxyInstanceCreatorCache.TryGetValue(genericProxyCreatorType, out proxyInstanceCreator))
            {
                proxyInstanceCreator = this.CreateProxyInstanceCreator(genericProxyCreatorType, binding, endpointAddress);

                this.proxyInstanceCreatorCache.Add(genericProxyCreatorType, proxyInstanceCreator);
            }
            return proxyInstanceCreator;
        }

        private Type MakeGenericType(Type serviceType)
        {
            if (!serviceType.IsInterface)
            {
                throw new InvalidOperationException("Type returned from proxy type provider must not be concrete type!");
            }

            Type genericProxyType;
            if (!this.genericTypeCache.TryGetValue(serviceType, out genericProxyType))
            {
                genericProxyType = typeof(ChannelFactory<>).MakeGenericType(new[] { serviceType });

                this.genericTypeCache.Add(serviceType, genericProxyType);
            }

            return genericProxyType;
        }
    }

The code can also be found on gist.github.com. Happy coding!

About the author

Daniel Marbach

2 comments

Recent Posts