WCF and external .config file

Hi all, it's been awhile. I've been pretty busy. Anyways, I just had a requirement, due to limitations of the software I'm working with, to load WCF configuration I had to do it from a file other than the web.config. I started out thinking this was going to be an easy task, wrong! Took a day of plugging away, but I finally found it.

My requirements were, load config from external file and be able to use the standard WCF config sections.

There are 3 main steps to this. Load the external config file. Get the configuration information for the services. Build the service objects from the config.

After a lot of framework dumpster diving (not sure where I heard it from, but it's awesome) I was finally able to figure out how to get the config sections I needed. And from there, with more diving, I was able to figure out how the objects were created.

Here is the resulting code. I have 2 classes. One of the wraps the ConfigLoader class that is used to load up the EndpointIdentity. I want to finish wrapping the ConfigLoader and see if there is anything I can remove and change to use the framework classes. These make use of reflection because of the classes and methods needed were marked as internal.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
 
namespace WcfStuff
{
    /// <summary>Provides a wrapper around the ConfigLoader class that is in the framework to create the WCF objects from config files. They marked the config loader as internal. This makes it public. Mwuahaha.
    /// </summary>
    public class ConfigLoader
    {
        private static Type _configLoaderType = null;
        public static Type ConfigLoaderType
        {
            get
            {
                return _configLoaderType;
            }
        }
 
        private object _configLoader = null;
        static ConfigLoader()
        {
            Assembly executingAssembly = Assembly.GetExecutingAssembly();
            AssemblyName[] referencedAssemblies = executingAssembly.GetReferencedAssemblies();
            string fullAssemblyName = null;
            Assembly serviceModelAssembly = null;
 
            foreach (AssemblyName name in referencedAssemblies)
            {
                if (name.Name.ToLower() == "system.servicemodel")
                {
                    fullAssemblyName = name.FullName;
                    break;
                }
            }
 
            serviceModelAssembly = Assembly.Load(fullAssemblyName);
            _configLoaderType = serviceModelAssembly.GetType("System.ServiceModel.Description.ConfigLoader");
 
            _configureEndpointAddress = ConfigLoaderType.GetMethod("ConfigureEndpointAddress", BindingFlags.NonPublic | BindingFlags.Static);
            _configureEndpointListenUriMethod = ConfigLoaderType.GetMethod("ConfigureEndpointListenUri", BindingFlags.NonPublic | BindingFlags.Static);
            _loadChannelBehaviorsInstanceMethod = ConfigLoaderType.GetMethod("LoadChannelBehaviors", BindingFlags.NonPublic | BindingFlags.Instance);
            _loadChannelBehaviorsStaticMethod = ConfigLoaderType.GetMethod("LoadChannelBehaviors", BindingFlags.NonPublic | BindingFlags.Static, null,
                new Type[] { typeof(string), typeof(ContextInformation), typeof(KeyedByTypeCollection<IEndpointBehavior>) }, null);
            _loadCommonClientBehaviorsMethod = ConfigLoaderType.GetMethod("LoadCommonClientBehaviors", BindingFlags.NonPublic | BindingFlags.Instance);
            _loadDefaultEndpointBehaviorsMethod = ConfigLoaderType.GetMethod("LoadDefaultEndpointBehaviors", BindingFlags.Public | BindingFlags.Static);
            _loadEndpointAddressMethod = ConfigLoaderType.GetMethod("LoadEndpointAddress", BindingFlags.NonPublic | BindingFlags.Static);
            _loadHostConfigMethod = ConfigLoaderType.GetMethod("LoadHostConfig", BindingFlags.Public | BindingFlags.Instance);
            _loadIdentityMethod = ConfigLoaderType.GetMethod("LoadIdentity", BindingFlags.NonPublic | BindingFlags.Static);
            _loadPolicyImportersMethod = ConfigLoaderType.GetMethod("LoadPolicyImporters", BindingFlags.NonPublic | BindingFlags.Static);
            _loadServiceDescriptionMethod = ConfigLoaderType.GetMethod("LoadServiceDescription", BindingFlags.Public | BindingFlags.Instance);
            _loadWsdlImportsMethod = ConfigLoaderType.GetMethod("LoadWsdlImporters", BindingFlags.NonPublic | BindingFlags.Static);
            _lookupBindingMethod = ConfigLoaderType.GetMethod("LookupBinding", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(string), typeof(string) }, null);
            _lookupComContractMethod = ConfigLoaderType.GetMethod("LookupComContract", BindingFlags.NonPublic | BindingFlags.Static);
            _lookupContractMethod = ConfigLoaderType.GetMethod("LookupContract", BindingFlags.Public | BindingFlags.Instance);
            _lookupContractForStandardEndpointMethod = ConfigLoaderType.GetMethod("LookupContractForStandardEndpoint", BindingFlags.Public | BindingFlags.Instance);
        }
 
        public ConfigLoader()
        {
            ConstructorInfo constructor = ConfigLoaderType.GetConstructor(Type.EmptyTypes);
            _configLoader = constructor.Invoke(null);
        }
 
        private static MethodInfo _configureEndpointAddress = null;
        public static MethodInfo ConfigureEndpointAddressMethod
        {
            get
            {
                return _configureEndpointAddress;
            }
        }
        
        public static void ConfigureEndpointAddress(ServiceEndpointElement serviceEndpointElement, ServiceHostBase host, ServiceEndpoint endpoint)
        {
            ConfigureEndpointAddressMethod.Invoke(null, new object[] { serviceEndpointElement, host, endpoint });
        }
 
        private static MethodInfo _configureEndpointListenUriMethod = null;
        public static MethodInfo ConfigureEndpointListenUriMethod
        {
            get
            {
                return _configureEndpointListenUriMethod;
            }
        }
        
        public static void ConfigureEndpointListenUri(ServiceEndpointElement serviceEndpointElement, ServiceHostBase host, ServiceEndpoint endpoint)
        {
            ConfigureEndpointListenUriMethod.Invoke(null, new object[] { serviceEndpointElement, host, endpoint });
        }
 
        private static MethodInfo _loadChannelBehaviorsInstanceMethod = null;
        public static MethodInfo LoadChannelBehaviorsInstanceMethod
        {
            get
            {
                return _loadChannelBehaviorsInstanceMethod;
            }
        }
        
        public void LoadChannelBehaviors(ServiceEndpoint serviceEndpoint, string configurationName)
        {
            LoadChannelBehaviorsInstanceMethod.Invoke(_configLoader, new object[] { serviceEndpoint, configurationName });
        }
 
        private static MethodInfo _loadChannelBehaviorsStaticMethod = null;
        public static MethodInfo LoadChannelBehaviorsStaticMethod
        {
            get
            {
                return _loadChannelBehaviorsStaticMethod;
            }
        }
        
        public static void LoadChannelBehaviors(string behaviorName, ContextInformation context, KeyedByTypeCollection<IEndpointBehavior> channelBehaviors)
        {
            LoadChannelBehaviorsStaticMethod.Invoke(null, new object[] { behaviorName, context, channelBehaviors });
        }
 
        private static MethodInfo _loadCommonClientBehaviorsMethod = null;
        public static MethodInfo LoadChannelBehaviorsMethod
        {
            get
            {
                return _loadCommonClientBehaviorsMethod;
            }
        }
        
        public void LoadCommonClientBehaviors(ServiceEndpoint serviceEndpoint)
        {
            LoadChannelBehaviorsMethod.Invoke(_configLoader, new object[] { serviceEndpoint });
        }
 
        private static MethodInfo _loadDefaultEndpointBehaviorsMethod = null;
        public static MethodInfo LoadDefaultEndpointBehaviorsMethod
        {
            get
            {
                return _loadDefaultEndpointBehaviorsMethod;
            }
        }
        
        public static void LoadDefaultEndpointBehaviors(ServiceEndpoint endpoint)
        {
            LoadDefaultEndpointBehaviorsMethod.Invoke(null, new object[] { endpoint });
        }
 
        private static MethodInfo _loadEndpointAddressMethod = null;
        public static MethodInfo LoadEndpointAddressMethod
        {
            get
            {
                return _loadEndpointAddressMethod;
            }
        }
        
        public static EndpointAddress LoadEndpointAddress(EndpointAddressElementBase element)
        {
            return (EndpointAddress)LoadEndpointAddressMethod.Invoke(null, new object[] { element });
        }
 
        private static MethodInfo _loadHostConfigMethod = null;
        public static MethodInfo LoadHostConfigMethod
        {
            get
            {
                return _loadHostConfigMethod;
            }
        }
        
        public void LoadHostConfig(ServiceElement serviceElement, ServiceHostBase host, Action<Uri> addBaseAddress)
        {
            LoadHostConfigMethod.Invoke(_configLoader, new object[] { host, addBaseAddress });
        }
 
        private static MethodInfo _loadIdentityMethod = null;
        public static MethodInfo LoadIdentityMethod
        {
            get { return _loadIdentityMethod; }
        }
        
        public static EndpointIdentity LoadIdentity(IdentityElement element)
        {
            return (EndpointIdentity)LoadIdentityMethod.Invoke(null, new object[] { element });
        }
 
        private static MethodInfo _loadPolicyImportersMethod = null;
        public static MethodInfo LoadPolicyImportersMethod
        {
            get { return _loadPolicyImportersMethod; }
        }
        
        public static Collection<IPolicyImportExtension> LoadPolicyImporters(PolicyImporterElementCollection policyImporterElements, ContextInformation context)
        {
            return (Collection<IPolicyImportExtension>)LoadPolicyImportersMethod.Invoke(null, new object[] { policyImporterElements, context });
        }
 
        private static MethodInfo _loadServiceDescriptionMethod = null;
        public static MethodInfo LoadServiceDescriptionMethod
        {
            get
            {
                return _loadServiceDescriptionMethod;
            }
        }
        
        public void LoadServiceDescription(ServiceHostBase host, ServiceDescription description, ServiceElement serviceElement, Action<Uri> addBaseAddress, bool skipHost = false)
        {
            LoadServiceDescriptionMethod.Invoke(_configLoader, new object[] { host, description, serviceElement, addBaseAddress, skipHost });
        }
 
        private static MethodInfo _loadWsdlImportsMethod = null;
        public static MethodInfo LoadWsdlImportsMethod
        {
            get
            {
                return _loadWsdlImportsMethod;
            }
        }
        
        public static Collection<IWsdlImportExtension> LoadWsdlImporters(WsdlImporterElementCollection wsdlImporterElements, ContextInformation context)
        {
            return (Collection<IWsdlImportExtension>)LoadWsdlImportsMethod.Invoke(null, new object[] { wsdlImporterElements, context });
        }
 
        private static MethodInfo _lookupBindingMethod = null;
        public static MethodInfo LookupBindingMethod
        {
            get
            {
                return _lookupBindingMethod;
            }
        }
        
        public static Binding LookupBinding(string bindingSectionName, string configurationName)
        {
            return (Binding)LookupBindingMethod.Invoke(null, new object[] { bindingSectionName, configurationName });
        }
 
        private static MethodInfo _lookupBindingContextMethod = null;
        public static MethodInfo LookupBindingContextMethod
        {
            get
            {
                return _lookupBindingContextMethod;
            }
        }
        
        public static Binding LookupBinding(string bindingSectionName, string configurationName, ContextInformation context)
        {
            return (Binding)LookupBindingContextMethod.Invoke(null, new object[] { bindingSectionName, configurationName, context });
        }
 
        private static MethodInfo _lookupComContractMethod = null;
        public static MethodInfo LookupComContractMethod
        {
            get
            {
                return _lookupComContractMethod;
            }
        }
        public static ComContractElement LookupComContract(Guid contractIID)
        {
            return (ComContractElement)LookupComContractMethod.Invoke(null, new object[] { contractIID });
        }
 
        private static MethodInfo _lookupContractMethod = null;
        public static MethodInfo LookupContractMethod
        {
            get
            {
                return _lookupContractMethod;
            }
        }
        
        public ContractDescription LookupContract(string contractName, string serviceName)
        {
            return (ContractDescription)_lookupContractMethod.Invoke(_configLoader, new object[] { contractName, serviceName });
        }
 
        private static MethodInfo _lookupContractForStandardEndpointMethod = null;
        public static MethodInfo LookupContractForStandardEndpointMethod
        {
            get
            {
                return _lookupContractForStandardEndpointMethod;
            }
        }
        
        public ContractDescription LookupContractForStandardEndpoint(string contractName, string serviceName)
        {
            return (ContractDescription)LookupContractForStandardEndpointMethod.Invoke(_configLoader, new object[] { contractName, serviceName });
        }
    }
}

And this is the class that does all of the work creating the service

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
 
namespace GuineaPigWindowsApp.WcfStuff
{
    public static class ServiceResolver
    {
        /// <summary>Looks at a config file and will create a new instance of T using the configured settings in the config file
        /// </summary>
        /// <typeparam name="I">Interface of the service contract</typeparam>
        /// <typeparam name="T">Service proxy to create</typeparam>
        /// <param name="configFilePath">Config file that is holding the settings.</param>
        /// <returns></returns>
        public static T Resolve<I, T>(string configFilePath)
            where I : class
            where T : ClientBase<I>, new()
        {
            Type type = typeof(I);
            string contract = type.FullName;
 
            return Resolve<I, T>(configFilePath, contract, null);
 
        }
        
        /// <summary>Looks at a config file and will create a new instance of T using the configured settings the config file
        /// </summary>
        /// <typeparam name="T">Service proxy to create</typeparam>
        /// <param name="configFilePath">Config file that is holding the settings.</param>
        /// <param name="endpointName">Name of the endpoint in the config file</param>
        /// <returns></returns>
        public static T Resolve<I, T>(string configFilePath, string endpointName)
            where I : class
            where T : ClientBase<I>, new()
        {
            return Resolve<I, T>(configFilePath, null, endpointName);
        }
        
        /// <summary>Looks at a config file and will create a new instance of T using the configured settings the config file. 
        /// If contractName is null, it will look for the endpointName. Either contractName or endpointName must be defined.
        /// </summary>
        /// <typeparam name="T">Service proxy to create</typeparam>
        /// <param name="configFilePath">Config file that is holding the settings.</param>
        /// <param name="contractName">Name of the contract in the config file</param>
        /// <param name="endpointName">Name of the endpoint in the config file</param>
        /// <returns></returns>
        public static T Resolve<I, T>(string configFilePath, string contractName, string endpointName)
            where I : class
            where T : ClientBase<I>, new()
        {
            if (contractName == null && endpointName == null)
            {
                throw new ArgumentException("Either contractName or endpointName must be assigned", "contractName, endpointName");
            }
            ExeConfigurationFileMap configMap = new ExeConfigurationFileMap();
            configMap.ExeConfigFilename = configFilePath;
 
            Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);
            ServiceModelSectionGroup serviceModelConfig = ServiceModelSectionGroup.GetSectionGroup(configuration);
 
            ChannelEndpointElement endpointElement = GetEndpointConfig(contractName, endpointName, serviceModelConfig);
            Binding binding = GetBinding(endpointElement.Binding, endpointElement.BindingConfiguration, serviceModelConfig);
            IEnumerable<IEndpointBehavior> behaviors = GetEndpointBehavior(endpointElement.BehaviorConfiguration, serviceModelConfig);
            EndpointAddress endpoint = GetEndpoint(endpointElement);
 
            T result = (T)Activator.CreateInstance(typeof(T), binding, endpoint);
 
            foreach (IEndpointBehavior behavior in behaviors)
            {
                result.Endpoint.Behaviors.Add(behavior);
            }
 
            return result;
        }
 
        public static Binding GetBinding(string name, string bindingConfiguration, ServiceModelSectionGroup serviceModelConfig)
        {
            //find the binding
            Binding result = null;
 
            foreach (BindingCollectionElement bindingCollection in serviceModelConfig.Bindings.BindingCollections)
            {
                if (bindingCollection.BindingName.ToLower() == name.ToLower())
                {
                    foreach (IBindingConfigurationElement bindingElement in bindingCollection.ConfiguredBindings)
                    {
                        if (bindingElement.Name.ToLower() == bindingConfiguration.ToLower())
                        {
                            result = (Binding)Activator.CreateInstance(bindingCollection.BindingType);
                            result.Name = bindingElement.Name;
                            bindingElement.ApplyConfiguration(result);
 
                            break;
                        }
                    }
 
                    if (result != null)
                    {
                        break;
                    }
                }
            }
 
            return result;
        }
        
        public static IEnumerable<IEndpointBehavior> GetEndpointBehavior(string name, ServiceModelSectionGroup serviceModelConfig)
        {
            BehaviorsSection behaviors = serviceModelConfig.Behaviors;
            EndpointBehaviorElementCollection endpointBehaviorsElements = behaviors.EndpointBehaviors;
            List<IEndpointBehavior> endpointBehaviors = new List<IEndpointBehavior>();
 
            if (!string.IsNullOrEmpty(name))
            {
                foreach (EndpointBehaviorElement behaviorElement in endpointBehaviorsElements)
                {
                    if (behaviorElement.Name == name)
                    {
                        foreach (BehaviorExtensionElement behaviorExtension in behaviorElement)
                        {
                            IEndpointBehavior behavior = (IEndpointBehavior)behaviorExtension.GetType().InvokeMember("CreateBehavior",
                                BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,
                                null, behaviorExtension, null);
 
                            endpointBehaviors.Add(behavior);
                        }
                        break;
                    }
                }
            }
 
            return endpointBehaviors.ToArray();
        }
        
        public static ChannelEndpointElement GetEndpointConfig(string contract, string name, ServiceModelSectionGroup serviceModelConfig)
        {
            foreach (ChannelEndpointElement endpoint in serviceModelConfig.Client.Endpoints)
            {
                if ((name == null || endpoint.Name.ToLower() == name.ToLower()) && (contract == null || endpoint.Contract.ToLower() == contract.ToLower()))
                {
                    return endpoint;
                }
            }
 
            return null;
        }
        
        public static EndpointAddress GetEndpoint(ChannelEndpointElement endpointConfig)
        {
            EndpointAddress result = null;
 
            result = new EndpointAddress(endpointConfig.Address, ConfigLoader.LoadIdentity(endpointConfig.Identity), endpointConfig.Headers.Headers);
 
            return result;
        }
    }
}