Programmatically setting WCF Configuration

It seems as if there is someone at Microsoft, who’s sole purpose in life is to figure out how I might want to use something (WCF), and then make sure I can’t use it in the way I want to. Today’s issue, programmatically configuring a WCF client application.

Background

I have created a central repository store in the form of a SQL database, that put simply, contains key/value pairs that I can query. All of my applications (or each machine.config) only need to specify a single database connection string to this configuration database.  All other configuration settings are retrieved from the database (and cached), making it easy to keep track of configuration, view all current configuration settings, and deploy the applications on new servers.

Expanding upon my simple definition, there is a second “key”, which corresponds to what environment (production, staging, development) the application is deployed. So my single configuration database controls the configuration of all applications in all environments. For those settings which are the same across all environments, no deployment key is specified.

So far this has worked great, and I wanted to add configuration information about a WCF client service.

WCF

WCF needs two properties set, a binding, and an endpoint, and each of these 2 properties have sub properties which may or may not have values that need to be set. Out of the box, WCF is most easily configured using application or web configuration files. All of the tools support the configuration files and it’s easy to see what’s going on (well sort of).

I basically wanted to take the XML sections that define the binding and endpoint, and place it into my configuration database. For some reason, I got it in my head that this would be really easy to do, but found out that it’s not. Searching thru Google yielded no direct solution. While you can programmatically set the Binding and Endpoint properties of a proxy class client object, I would either have to have 1 setting per property (and sub property) in my configuration database, or come up with an XML schema, parse the schema and set the values. This was the path I was pursing, but instead of making my own schema, I just used the XML configuration schema provided out of the box, in hopes that one day I could just pass those sections to binding or endpoint objects and have it parse for me.

After creating classes to parse a single binding (WsHttpBinding), I decided there must be a better way.  The code to parse the configuration files must exist somewhere in System.ServiceModel, so I decided to go spelunking using reflector to see what was going on, and determine if I could hack something together.

Implementation

Overview

After spending a couple of hours in reflector, I realized the main roadblock was the use of the System.Configuration namespace to handle the configuration data for System.ServiceModel. System.Configuration only supports the file system, and provides no extension methods at this time (something I’d like to see from Microsoft in the future).

This left me with one option, which was to create temporary XML file with my configuration data from my database, and then read that configuration file using the proper classes/methods in System.Configuration. It took me awhile to accept this option, as writing a temporary file seemed messy, but then I realized, temporary files are a fact of life in .Net, programming, operating systems, etc.

Configuration Store

My configuration store was already defined as I mentioned previously. The 2 XML samples below are stored as separate entries in my configuration database. Below is the XML I stored for handling the bindings:

<bindings>
 <wsHttpBinding>
  <binding name="WSHttpBinding_ITwoWayAsyncVoid" closeTimeout="00:01:00"
          openTimeout="00:00:30" receiveTimeout="00:02:00" sendTimeout="00:00:15"
          bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
          maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
          textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <reliableSession ordered="true" inactivityTimeout="00:10:00"
            enabled="false" />
          <security mode="Message">
            <transport clientCredentialType="Windows" proxyCredentialType="None"
              realm="" />
            <message clientCredentialType="Windows" negotiateServiceCredential="true"
              algorithmSuite="Default" establishSecurityContext="true" />
          </security>
  </binding>
 </wsHttpBinding>
</bindings>
 

I am able to store multiple bindings, and binding configurations in a single entry in my configuration database (see below). In this example, I only have a single binding and binding configuration. I was originally planning to have 1 entry for each binding, but I don’t think that will be necessary, if anything I will have a single entry for all necessary bindings per environment, and even that might be overkill.

This is the XML I stored for the end point:

<bindings>
 <wsHttpBinding>
  <binding name="WSHttpBinding_ITwoWayAsyncVoid" />
 </wsHttpBinding>
</bindings>
<client>
 <endpoint address="http://tersodemodev1/TsiCbsEsbReciever/WcfService_TersoSolutions_CBS_Fusion_BizTalk_InitialProcess.svc"
        binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ITwoWayAsyncVoid"
        contract="TsiCbsEsbReceiver.WcfService_TersoSolutions_CBS_Fusion_BizTalk_InitialProcess"
        name="WSHttpBinding_ITwoWayAsyncVoid">
        <identity>
          <userPrincipalName value="TERSODEMODEV1BizTalkWebServices" />
        </identity>
 </endpoint>
</client>
Notice the repeat of the <bindings> section. This was required if I wanted to use the the classes in System.ServiceModel for parsing the endpoint, I needed to define a skeleton for the bindings.
I separated the endpoints from the bindings, as that is what is going to change between environments the most for me. The endpoint specifies which server (test, production, staging), and identity (for single server test scenarios, the account will be local, for multi server production enviroments, the identity will be a domain account).
Code
I created 2 classes under a namespace called ConfigSystem. The first class, called ConfigMgr contains code that allows me to go from my configuration database to a System.Configuration.Configuration object by way of a temporary file. The second class called Wcf, contains code specific to my Wcf configuration implementation, and contains code inferred from reflector.
All of the code in ConfigMgr uses public classes and is pretty simple.
public static class ConfigMgr
 {
  #region MemberVars
  private const string configFileHeader = "<?xml version="1.0"?><configuration>";
  private const string configFileFooter = "</configuration>";
  
  #endregion

  #region Methods - Public

  public static ConfigurationSection GetSection(string configKey,
   string xmlConfigFileHeaderExtra, string xmlConfigFileFooterExtra, string sectionName)
  {
   //Get an xml document from the configuration database to store as a tempory file.
   System.Xml.XmlDocument doc = PrepareXmlDocument(configKey, xmlConfigFileHeaderExtra, xmlConfigFileFooterExtra);

   //Now write out to the temp file.
   string path = System.IO.Path.GetTempFileName();
   using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(path))
   {
    doc.WriteTo(writer);
    writer.Close();
   }
   
   //Setup configuration file map so that we can use standard file based config file
   ExeConfigurationFileMap map = new ExeConfigurationFileMap();
   map.ExeConfigFilename = path;

   System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
   
   //Now return the specified section
   return config.GetSection(sectionName);
  }

  private static System.Xml.XmlDocument PrepareXmlDocument(string configKey, string xmlConfigFileHeaderExtra, string xmlConfigFileFooterExtra)
  {
   System.Text.StringBuilder sb = new System.Text.StringBuilder();
   
   //Append header
   sb.Append(configFileHeader);
   sb.Append(xmlConfigFileHeaderExtra);

   sb.Append(ConfigHandler.GetValue(configKey));

   //Append Footer
   sb.Append(xmlConfigFileFooterExtra);
   sb.Append(configFileFooter);

   System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
   doc.LoadXml(sb.ToString());
   return doc;
  }

  #endregion
 }
The code in the Wcf class uses some methods I copied via reflector, and because I don’t know the legal ramifications of that, I am only providing an outline of what I did.
GetBinding:
  • Calls ConfigMgr.Get section and returns the <bindings> section. This is cast to a BindingsSection object.
  • Code taken from System.ServiceModel.Configuration creates a BindingCollectionElement which is the correct type associated with the binding stored in the XML config.
  • A new binding object is created using Activator.CreateInstance(BindingCollectionElement.BindingType)
  • Additional code taken from System.ServiceModel.Configuration loops thru BindingCollectionElement.ConfiguredBindings and initializes the binding object previously created. A check is made to make sure multiple bindings of the same type are not created.
  • Finally, the binding object is returned to the caller.

GetChannelEndpointElement: This class uses only public classes and methods so I am including it. EnpointElement contains the configuration data for an endpoint, including the URI, identity, binding, and  binding configuration. The binding and binding configuration data is actually passed to my GetBinding method to return the correct binding with the correct binding configuration.

public static ChannelEndpointElement GetChannelEndpointElement(string configKey)
  {
   ClientSection section = (ClientSection)ConfigMgr.GetSection(configKey, system_ServiceModelAsOpenXml, system_ServiceModelAsCloseXml, clientSection);

   if (section == null)
    throw new ArgumentException(string.Format("Client section returned from config db for key {0} was null", configKey));

   if (section.Endpoints.Count != 1)
    throw new ArgumentException(string.Format("There must be exactly 1 endpoint returned from the configuration. {0} endpoints were returned for key {1}", section.Endpoints.Count, configKey));

   return section.Endpoints[0];
   
  }
GetEndpointAddress: Another method I created which almost uses all public methods. It takes a ChannelEnpointElement and returns a EndpointAddress which can be assigned to a wcf proxy class.
public static EndpointAddress GetEndpointAddress(ChannelEndpointElement element)
  {
   //Create a new builder, as the endpoint address is an immutable class
   EndpointAddressBuilder builder = new EndpointAddressBuilder();

   builder.Identity = LoadIdentity(element.Identity);
   builder.Uri = element.Address;

   return builder.ToEndpointAddress();
  }
LoadIdentity: This final method was taken from System.ServiceModel.Description.Configloader. This method takes a IdentityElement (a property of ChannelEnpointElement) and returns a EndpointIdentity which is passed to the EndpointAddressBuilder in my GetEndpointAddress method. The method basically checks the the existence of certain properties, and if one exists, it returns the appropriate identity using public factory methods.
  • EndpointIdentity.CreateUpnIdentity
  • EndpointIdentity.CreateSpnIdentity
  • EndpointIdentity.CreateDnsIdentity
  • EndpointIdentity.CreateRsaIdentity
  • EndpointIdentity.CreateX509CertificateIdentity

There is a identity type of certificate reference which I was unable to add support for, because it referenced additional internal classes which I didn’t want to duplicate at this time since I had no need for that identity type.

Issues

Since this is completely unsupported by Microsoft, and uses code that was never meant for external use, in a future version of the framework, something could break. I’m also not an legal expert when it comes with what someone can do with MSIL code discovered thru reflector, so for now, the actual code I wrote is not available publicly. Hopefully I provided enough information to allow someone to reproduce my steps and implement something on their own.

Another somewhat disappointing realization I came to, was that this might make it hard to support write operations to my configuration database. Currently the configuration database is managed thru scripts and direct editing. If write support to the config database became a requirement, I’d probably try to do the reverse and use the built in file save functionality in System.Configuration to save a temporary xml file, read it and then store it into my config database.

Conclusion

This was my first attempt at doing something like this, digging into framework code to see how it really works and trying to come up with a solution. I’m going to go forward using the code I wrote outlined in this post, as I have full read support for WCF XML configuration. The amount of code I had to write and test is significantly less then if I would have wanted to get the same amount of functionality (configuration support for all bindings and endpoint configurations).

2 thoughts on “Programmatically setting WCF Configuration

    • I havn’t revisited this since I wrote the post. I’m kind of surprised how much traffic it gets.