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;
}
}
}