Click here to Skip to main content
15,886,720 members
Articles / Programming Languages / C# 4.0

WCF Proxy Manager - Going Configless

Rate me:
Please Sign up or sign in to vote.
4.80/5 (8 votes)
17 Apr 2012CPOL5 min read 99.8K   601   36  
Making WCF Config Lite, easy for developers, and durable ... yup durable without ping.
using System;
using System.Collections;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using WcfProxyManager.Behaviors;
using WcfProxyManager.Helpers;

namespace WcfProxyManager
{
    /// <summary>
    /// </summary>
    public static class ProxyManager
    {
        //Our Ubber Brand New Cool cache Objects
        private static readonly Hashtable _channelFactoryCache = new Hashtable();
        private static readonly Hashtable _proxyCache = new Hashtable();
        private static readonly Hashtable _dnsCache = new Hashtable();


        // Return an Encapsulated IClientChannel
        public static T GetProxy<T>()
        {
            var t = typeof (T);
            if (!_proxyCache.ContainsKey(t))
            {
                try
                {
                    _proxyCache.Add(t, createProxy<T>());
                }
                catch (Exception ex)
                {
                    throw new Exception("Failed to create provider: " + ex.Message, ex);
                }
            }
            var s = (ProxyBase<T>) _proxyCache[t];
            var ic = (IClientChannel) s.InnerChannel;
            //here is the key Abort anything and force dispose
            ic.Abort();
            // Recreate the channel there is a small amount of overhead here about 4-5 milliseconds 
            // Well worth the ease of deployment / durability
            s.SetChannel();
            return s.InnerChannel;
        }

        /// <summary>
        ///   This is where we new up an encapsulated IClientChannel
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <returns> </returns>
        internal static ProxyBase<T> createProxy<T>()
        {
            var t = typeof (T);
            // Get The Channel Factory or create it if needed
            ChannelFactory channelFactory;

            lock (_channelFactoryCache)
            {
                if (_channelFactoryCache.ContainsKey(t))
                {
                    channelFactory = (ChannelFactory) _channelFactoryCache[t];
                }
                else
                {
                    channelFactory = createChannelFactory<T>();
                    _channelFactoryCache.Add(t, channelFactory);
                }
            }
            EndpointAddress endpoint = null;

            //get Configuration
            var s = ConfigurationHelper.GetKey("HOST", Environment.MachineName);
            var port = ConfigurationHelper.GetKey("PORT", "8080");
            var binding = ConfigurationHelper.GetKey("BINDING", "HTTP");

            var serviceName = typeof (T).ToString();
            //Ser the correct service name Defaults to the interface name minus the I
            if (serviceName[0] == char.Parse("I"))
            {
                serviceName = serviceName.Remove(0, 1);
            }
            //Create the URI
            string server;
            switch (binding)
            {
                case "TCP":
                    server = string.Format("net.tcp://" + getIPAddress(s) + ":{0}/{1}", port, serviceName);
                    endpoint = new EndpointAddress(server);
                    break;
                case "HTTP":
                    server = string.Format("http://" + getIPAddress(s) + ":{0}/{1}", port, serviceName);
                    endpoint = new EndpointAddress(server);
                    break;
            }

            //Create the Enapsulated IClientChanenel
            var pb = new ProxyBase<T>((ChannelFactory<T>) channelFactory, endpoint);

            return pb;
        }

        // For speed I have us resolving to the Ip of the end 
        // Address.  This is probally totally not needed 
        private static string getIPAddress(string server)
        {
            lock (_dnsCache)
            {
                if (!_dnsCache.ContainsKey(server))
                {
                    var host = Dns.GetHostEntry(server);
                    _dnsCache.Add(server, host.AddressList[0].ToString());
                }

                return _dnsCache[server].ToString();
            }
        }

        /// <summary>
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <returns> </returns>
        /// <exception cref="ArgumentException"></exception>
        private static ChannelFactory createChannelFactory<T>()
        {
            Binding b = null;
            switch (ConfigurationHelper.GetKey("BINDING", "HTTP"))
            {
                case "HTTP":
                    b = new BasicHttpBinding();

                    break;
                case "TCP":
                    b = new NetTcpBinding();
                    break;
            }

            if (b != null)
            {
                var factory = new ChannelFactory<T>(b);
                // This is super important
                // Why ? This is where you can add
                // Custom behaviors to your outgoing calls
                // like Message Inspectors ... or behaviors.
                ApplyContextToChannelFactory(factory);

                return factory;
            }
            return null;
        }

        /// <summary>
        /// </summary>
        /// <param name="factory"> </param>
        public static void ApplyContextToChannelFactory(ChannelFactory factory)
        {
            //This area in where custom nehaviors are added 
            if (factory == null)
            {
                throw new ArgumentException("The argument 'factory' cannot be null.");
            }

            // here is an example of a behavior swap were some default
            // values where set on the replacement
            foreach (var desc in factory.Endpoint.Contract.Operations)
            {
                desc.Behaviors.Remove<DataContractSerializerOperationBehavior>();
                desc.Behaviors.Add(new ReferencePreservingBehavior(desc));
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect ESPN
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions