Configuration and Settings Management

Source code and code generation code for this project can be found at: https://retkomma.wordpress.com/2010/04/17/source-code-for-settings-management-project/

An intrinsic problem of the .NET framework is the .config-design. This design means that every client application can access only its own configurations. Therefore, in an enterprise application, configurations often get duplicated and it gets tough for the IT pros to change username/passwords, servernames etc.

A great “anti” example is WCF configurations. Each time a client consumes a WCF service it must duplciate the relevant configuration in its app.config or web.config file. Thus, changing an endpoint or a binding is definitely not trivial, and creating a new client involves a lot copy-pasting.

Now, the only place I want my configurations is in the DB. In other words, this is how I want my .config file to look like:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="serverName" value="m8-app"/>
</appSettings>
</configuration>

One information only, namely a servername. The only purpose of this servername is to be able to retrieve the rest of the configurations.

The instantiation of the WCF services requires a minor setup. I’ll try to walk you through it.

In ‘headlines’ this is what it looks like:

1. The client creates the WCF service proxy class by using a factory method.
2. The client service factory retrieves configuration information from the server via an ashx service.
3. The ashx service queries the database for service information using the Enterprise Application Block Library.
4. The ashx service serializes the configuration information to the service response.
5. The client service factory deserializes the configuration information.
6. The client service factory programmatically creates a binding and an endpoint address.
7. The client service factory creates an instance of the service cilent.

Now, this requires a couple of coding hours, but if your app is enterprise, it will leave you with a reusable WCF Service Geateway with absolutely NO dependency to a .config file. It will also allow you to maintain your bindings one place only, i.e. in the database.

1. The client creates the WCF service proxy class by using a factory method.
The client is instantiated by e.g.:

DocService.DocumentServiceClient dsc = new ServiceFactory().CreateDocumentServiceClient();

2. The client service factory retrieves configuration information from the server via an ashx service.
First, the ServiceFactory contains some fields:


String serverName;
Binding binding;
EndpointAddress address;

The service factory calls the ashx service like this:


WebRequest request = WebRequest.Create(String.Format("http://{0}/Point5Server/CommunicationConfigurationGateway.ashx?name={1}", serverName, configurationName));
WebResponse response = request.GetResponse();

3. The ashx service queries the database for service information using the Enterprise Application Block Library.

The ashx service queries the DB like this:


String configurationName = context.Request.QueryString["name"];
if (String.IsNullOrEmpty(configurationName))
return;

String connectionString = ConfigurationManager.ConnectionStrings["GlobalConnectionString"].ConnectionString;
String getSproc = "EntLib_GetConfig";
String setSproc = "EntLib_SetConfig";
String refreshSproc = "UpdateSectionDate";
String removeSproc = "EntLib_RemoveSection";

SqlConfigurationSource source = new SqlConfigurationSource(connectionString, getSproc, setSproc, refreshSproc, removeSproc);
ClientCommunicationConfiguration conf = source.GetSection(configurationName) as ClientCommunicationConfiguration;

4. The ashx service serializes the configuration information to the service response.
The ashx service now serializes the ClientCommunicationConfiguration and sends it to the response:

MemoryStream ms = new MemoryStream();
using (XmlWriter xmlwriter = System.Xml.XmlWriter.Create(ms))
{
conf.WriteXml(xmlwriter);
xmlwriter.Flush();
}
ms.Seek(0, SeekOrigin.Begin);

context.Response.ContentType = "text/xml";
context.Response.Write(Encoding.UTF8.GetString(ms.ToArray()));
context.Response.Flush();

5. The client service factory deserializes the configuration information.
The client now recieves the serialized configuration information. The first task of the client is to deserialize the information:

Stream s = response.GetResponseStream();
XmlReader reader = XmlReader.Create(s);
ClientCommunicationConfiguration conf = new ClientCommunicationConfiguration();
conf.ReadXml(reader);

6. The client service factory programmatically creates a binding and an endpoint address.
Now that the client has the configuration information, it has to create the binding and the endpoint. This is done in a factory method. The full method is:

public Binding CreateBinding(ClientCommunicationConfiguration conf)
{
Binding binding = null;

if (conf.BindingType == "WSHttpBinding" || conf.BindingType == "wsHttpBinding")
{
WSHttpBinding wsBinding = new WSHttpBinding();
wsBinding.SendTimeout = TimeSpan.FromSeconds(conf.BindingSendTimeout);
wsBinding.OpenTimeout = TimeSpan.FromSeconds(conf.BindingOpenTimeout);
wsBinding.ReceiveTimeout = TimeSpan.FromSeconds(conf.BindingReceiveTimeout);
wsBinding.CloseTimeout = TimeSpan.FromSeconds(conf.BindingCloseTimeout);
wsBinding.AllowCookies = conf.BindingAllowCookies;
wsBinding.BypassProxyOnLocal = conf.BindingBypassProxyOnLocal;
wsBinding.MaxBufferPoolSize = conf.BindingMaxBufferPoolSize;
wsBinding.MaxReceivedMessageSize = conf.BindingMaxReceivedMessageSize;
wsBinding.MessageEncoding = (WSMessageEncoding)Enum.Parse(typeof(WSMessageEncoding), conf.BindingMessageEncoding);
wsBinding.Name = conf.BindingName;
wsBinding.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas();
wsBinding.ReaderQuotas.MaxArrayLength = conf.ReaderQuotasMaxArrayLength;
wsBinding.ReaderQuotas.MaxBytesPerRead = conf.ReaderQuotasMaxBytesPerRead;
wsBinding.ReaderQuotas.MaxDepth = conf.ReaderQuotasMaxDepth;
wsBinding.ReaderQuotas.MaxNameTableCharCount = conf.ReaderQuotasMaxNameTableCharCount;
wsBinding.ReaderQuotas.MaxStringContentLength = conf.ReaderQuotasMaxStringContentLength;

if (conf.ReliableSessionEnabled)
{
ReliableSessionBindingElement reliableSessionElement = new ReliableSessionBindingElement();
wsBinding.ReliableSession.Enabled = conf.ReliableSessionEnabled;
wsBinding.ReliableSession.InactivityTimeout = TimeSpan.FromSeconds(conf.ReliableSessionInactivityTimeout);
wsBinding.ReliableSession.Ordered = conf.ReliableSessionOrdered;
}

wsBinding.Security.Mode = (SecurityMode)Enum.Parse(typeof(SecurityMode), conf.SecurityMode);
wsBinding.Security.Message.ClientCredentialType = (MessageCredentialType)Enum.Parse(typeof(MessageCredentialType), conf.MessageClientCredentialType);
wsBinding.Security.Transport.ClientCredentialType = (HttpClientCredentialType)Enum.Parse(typeof(HttpClientCredentialType), conf.TransportClientCredentialType);
wsBinding.TextEncoding = Encoding.GetEncoding(conf.BindingTextEncoding);
wsBinding.TransactionFlow = conf.BindingTransactionFlow;
wsBinding.UseDefaultWebProxy = conf.BindingUseDefaultWebProxy;

binding = wsBinding as Binding;
}
return binding;
}{
Binding binding = null;

if (conf.BindingType == "WSHttpBinding" || conf.BindingType == "wsHttpBinding")
{
WSHttpBinding wsBinding = new WSHttpBinding();
wsBinding.SendTimeout = TimeSpan.FromSeconds(conf.BindingSendTimeout);
wsBinding.OpenTimeout = TimeSpan.FromSeconds(conf.BindingOpenTimeout);
wsBinding.ReceiveTimeout = TimeSpan.FromSeconds(conf.BindingReceiveTimeout);
wsBinding.CloseTimeout = TimeSpan.FromSeconds(conf.BindingCloseTimeout);
wsBinding.AllowCookies = conf.BindingAllowCookies;
wsBinding.BypassProxyOnLocal = conf.BindingBypassProxyOnLocal;
wsBinding.MaxBufferPoolSize = conf.BindingMaxBufferPoolSize;
wsBinding.MaxReceivedMessageSize = conf.BindingMaxReceivedMessageSize;
wsBinding.MessageEncoding = (WSMessageEncoding)Enum.Parse(typeof(WSMessageEncoding), conf.BindingMessageEncoding);
wsBinding.Name = conf.BindingName;
wsBinding.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas();
wsBinding.ReaderQuotas.MaxArrayLength = conf.ReaderQuotasMaxArrayLength;
wsBinding.ReaderQuotas.MaxBytesPerRead = conf.ReaderQuotasMaxBytesPerRead;
wsBinding.ReaderQuotas.MaxDepth = conf.ReaderQuotasMaxDepth;
wsBinding.ReaderQuotas.MaxNameTableCharCount = conf.ReaderQuotasMaxNameTableCharCount;
wsBinding.ReaderQuotas.MaxStringContentLength = conf.ReaderQuotasMaxStringContentLength;

if (conf.ReliableSessionEnabled)
{
ReliableSessionBindingElement reliableSessionElement = new ReliableSessionBindingElement();
wsBinding.ReliableSession.Enabled = conf.ReliableSessionEnabled;
wsBinding.ReliableSession.InactivityTimeout = TimeSpan.FromSeconds(conf.ReliableSessionInactivityTimeout);
wsBinding.ReliableSession.Ordered = conf.ReliableSessionOrdered;
}

wsBinding.Security.Mode = (SecurityMode)Enum.Parse(typeof(SecurityMode), conf.SecurityMode);
wsBinding.Security.Message.ClientCredentialType = (MessageCredentialType)Enum.Parse(typeof(MessageCredentialType), conf.MessageClientCredentialType);
wsBinding.Security.Transport.ClientCredentialType = (HttpClientCredentialType)Enum.Parse(typeof(HttpClientCredentialType), conf.TransportClientCredentialType);
wsBinding.TextEncoding = Encoding.GetEncoding(conf.BindingTextEncoding);
wsBinding.TransactionFlow = conf.BindingTransactionFlow;
wsBinding.UseDefaultWebProxy = conf.BindingUseDefaultWebProxy;

binding = wsBinding as Binding;
}
return binding;
}

The endponit address is created the same way.

7. The client service factory creates an instance of the service cilent.
The client can now create the service client:

binding = new ClientCommunicationConfigurationFactory().CreateBinding(conf);
address = new ClientCommunicationConfigurationFactory().CreateEndpoint(conf)
CaseServiceClient csc = new CaseServiceClient(binding, address);

Thats it! No more f****** configuration files!

Please note that once you have this framework, it is reuseable for all your configurations. Also, unlike the .config configurations you will have intellisense in your configuration classes, since they are real classes.

Nb. The full configuration proxy class is listet here:


public class ClientCommunicationConfiguration : SerializableConfigurationSection
{
private const String endpointAddress = "endpointAddress";
[ConfigurationProperty(endpointAddress)]
public String EndpointAddress
{
get { return base[endpointAddress] as String; }
set { base[endpointAddress] = value; }
}
private const String endpointBinding = "endpointBinding";
[ConfigurationProperty(endpointBinding)]
public String EndpointBinding
{
get { return base[endpointBinding] as String; }
set { base[endpointBinding] = value; }
}
private const String endpointBindingConfiguration = "endpointBindingConfiguration";
[ConfigurationProperty(endpointBindingConfiguration)]
public String EndpointBindingConfiguration
{
get { return base[endpointBindingConfiguration] as String; }
set { base[endpointBindingConfiguration] = value; }
}
private const String endpointContract = "endpointContract";
[ConfigurationProperty(endpointContract)]
public String EndpointContract
{
get { return base[endpointContract] as String; }
set { base[endpointContract] = value; }
}
private const String endpointName = "endpointName";
[ConfigurationProperty(endpointName)]
public String EndpointName
{
get { return base[endpointName] as String; }
set { base[endpointName] = value; }
}
private const String identityServerPrincipalName = "identityServerPrincipalName";
[ConfigurationProperty(identityServerPrincipalName)]
public String IdentityServerPrincipalName
{
get { return base[identityServerPrincipalName] as String; }
set { base[identityServerPrincipalName] = value; }
}
private const String identityDns = "identityDns";
[ConfigurationProperty(identityDns)]
public String IdentityDns
{
get { return base[identityDns] as String; }
set { base[identityDns] = value; }
}
private const String bindingType = "bindingType";
[ConfigurationProperty(bindingType)]
public String BindingType
{
get { return base[bindingType] as String; }
set { base[bindingType] = value; }
}
private const String bindingName = "bindingName";
[ConfigurationProperty(bindingName)]
public String BindingName
{
get { return base[bindingName] as String; }
set { base[bindingName] = value; }
}
private const String bindingCloseTimeout = "bindingCloseTimeout";
[ConfigurationProperty(bindingCloseTimeout)]
public Double BindingCloseTimeout
{
get { return (Double)base[bindingCloseTimeout]; }
set { base[bindingCloseTimeout] = value; }
}
private const String bindingTransactionFlow = "bindingTransactionFlow";
[ConfigurationProperty(bindingTransactionFlow)]
public Boolean BindingTransactionFlow
{
get { return (Boolean)base[bindingTransactionFlow]; }
set { base[bindingTransactionFlow] = value; }
}
private const String bindingBypassProxyOnLocal = "bindingBypassProxyOnLocal";
[ConfigurationProperty(bindingBypassProxyOnLocal)]
public Boolean BindingBypassProxyOnLocal
{
get { return (Boolean)base[bindingBypassProxyOnLocal]; }
set { base[bindingBypassProxyOnLocal] = value; }
}
private const String bindingSendTimeout = "bindingSendTimeout";
[ConfigurationProperty(bindingSendTimeout)]
public Double BindingSendTimeout
{
get { return (Double)base[bindingSendTimeout]; }
set { base[bindingSendTimeout] = value; }
}
private const String bindingReceiveTimeout = "bindingReceiveTimeout";
[ConfigurationProperty(bindingReceiveTimeout)]
public Double BindingReceiveTimeout
{
get { return (Double)base[bindingReceiveTimeout]; }
set { base[bindingReceiveTimeout] = value; }
}
private const String bindingOpenTimeout = "bindingOpenTimeout";
[ConfigurationProperty(bindingOpenTimeout)]
public Double BindingOpenTimeout
{
get { return (Double)base[bindingOpenTimeout]; }
set { base[bindingOpenTimeout] = value; }
}
private const String bindingTextEncoding = "bindingTextEncoding";
[ConfigurationProperty(bindingTextEncoding)]
public String BindingTextEncoding
{
get { return base[bindingTextEncoding] as String; }
set { base[bindingTextEncoding] = value; }
}
private const String bindingMessageEncoding = "bindingMessageEncoding";
[ConfigurationProperty(bindingMessageEncoding)]
public String BindingMessageEncoding
{
get { return base[bindingMessageEncoding] as String; }
set { base[bindingMessageEncoding] = value; }
}
private const String bindingMaxReceivedMessageSize = "bindingMaxReceivedMessageSize";
[ConfigurationProperty(bindingMaxReceivedMessageSize)]
public Int32 BindingMaxReceivedMessageSize
{
get { return (Int32)base[bindingMaxReceivedMessageSize]; }
set { base[bindingMaxReceivedMessageSize] = value; }
}
private const String bindingMaxBufferPoolSize = "bindingMaxBufferPoolSize";
[ConfigurationProperty(bindingMaxBufferPoolSize)]
public Int64 BindingMaxBufferPoolSize
{
get { return (Int64)base[bindingMaxBufferPoolSize]; }
set { base[bindingMaxBufferPoolSize] = value; }
}
private const String bindingHostNameComparisonMode = "bindingHostNameComparisonMode";
[ConfigurationProperty(bindingHostNameComparisonMode)]
public String BindingHostNameComparisonMode
{
get { return base[bindingHostNameComparisonMode] as String; }
set { base[bindingHostNameComparisonMode] = value; }
}
private const String readerQuotasMaxNameTableCharCount = "readerQuotasMaxNameTableCharCount";
[ConfigurationProperty(readerQuotasMaxNameTableCharCount)]
public Int32 ReaderQuotasMaxNameTableCharCount
{
get { return (Int32)base[readerQuotasMaxNameTableCharCount]; }
set { base[readerQuotasMaxNameTableCharCount] = value; }
}
private const String readerQuotasMaxStringContentLength = "readerQuotasMaxStringContentLength";
[ConfigurationProperty(readerQuotasMaxStringContentLength)]
public Int32 ReaderQuotasMaxStringContentLength
{
get { return (Int32)base[readerQuotasMaxStringContentLength]; }
set { base[readerQuotasMaxStringContentLength] = value; }
}
private const String readerQuotasMaxDepth = "readerQuotasMaxDepth";
[ConfigurationProperty(readerQuotasMaxDepth)]
public Int32 ReaderQuotasMaxDepth
{
get { return (Int32)base[readerQuotasMaxDepth]; }
set { base[readerQuotasMaxDepth] = value; }
}
private const String bindingAllowCookies = "bindingAllowCookies";
[ConfigurationProperty(bindingAllowCookies)]
public Boolean BindingAllowCookies
{
get { return (Boolean)base[bindingAllowCookies]; }
set { base[bindingAllowCookies] = value; }
}
private const String bindingUseDefaultWebProxy = "bindingUseDefaultWebProxy";
[ConfigurationProperty(bindingUseDefaultWebProxy)]
public Boolean BindingUseDefaultWebProxy
{
get { return (Boolean)base[bindingUseDefaultWebProxy]; }
set { base[bindingUseDefaultWebProxy] = value; }
}
private const String reliableSessionEnabled = "reliableSessionEnabled";
[ConfigurationProperty(reliableSessionEnabled)]
public Boolean ReliableSessionEnabled
{
get { return (Boolean)base[reliableSessionEnabled]; }
set { base[reliableSessionEnabled] = value; }
}
private const String reliableSessionInactivityTimeout = "reliableSessionInactivityTimeout";
[ConfigurationProperty(reliableSessionInactivityTimeout)]
public Int32 ReliableSessionInactivityTimeout
{
get { return (Int32)base[reliableSessionInactivityTimeout]; }
set { base[reliableSessionInactivityTimeout] = value; }
}
private const String reliableSessionOrdered = "reliableSessionOrdered";
[ConfigurationProperty(reliableSessionOrdered)]
public Boolean ReliableSessionOrdered
{
get { return (Boolean)base[reliableSessionOrdered]; }
set { base[reliableSessionOrdered] = value; }
}
private const String readerQuotasMaxArrayLength = "readerQuotasMaxArrayLength";
[ConfigurationProperty(readerQuotasMaxArrayLength)]
public Int32 ReaderQuotasMaxArrayLength
{
get { return (Int32)base[readerQuotasMaxArrayLength]; }
set { base[readerQuotasMaxArrayLength] = value; }
}

private const String readerQuotasMaxBytesPerRead = "readerQuotasMaxBytesPerRead";
[ConfigurationProperty(readerQuotasMaxBytesPerRead)]
public Int32 ReaderQuotasMaxBytesPerRead
{
get { return (Int32)base[readerQuotasMaxBytesPerRead]; }
set { base[readerQuotasMaxBytesPerRead] = value; }
}
private const String messageClientCredentialType = "messageClientCredentialType";
[ConfigurationProperty(messageClientCredentialType)]
public String MessageClientCredentialType
{
get { return base[messageClientCredentialType] as String; }
set { base[messageClientCredentialType] = value; }
}
private const String messageProxyCredentialType = "messageProxyCredentialType";
[ConfigurationProperty(messageProxyCredentialType)]
public String MessageProxyCredentialType
{
get { return base[messageProxyCredentialType] as String; }
set { base[messageProxyCredentialType] = value; }
}
private const String messageRealm = "messageRealm";
[ConfigurationProperty(messageRealm)]
public String MessageRealm
{
get { return base[messageRealm] as String; }
set { base[messageRealm] = value; }
}
private const String transportProxyCredentialType = "transportProxyCredentialType";
[ConfigurationProperty(transportProxyCredentialType)]
public String TransportProxyCredentialType
{
get { return base[transportProxyCredentialType] as String; }
set { base[transportProxyCredentialType] = value; }
}
private const String transportRealm = "transportRealm";
[ConfigurationProperty(transportRealm)]
public String TransportRealm
{
get { return base[transportRealm] as String; }
set { base[transportRealm] = value; }
}
private const String transportClientCredentialType = "transportClientCredentialType";
[ConfigurationProperty(transportClientCredentialType)]
public String TransportClientCredentialType
{
get { return base[transportClientCredentialType] as String; }
set { base[transportClientCredentialType] = value; }
}
private const String securityMode = "securityMode";
[ConfigurationProperty(securityMode)]
public String SecurityMode
{
get { return base[securityMode] as String; }
set { base[securityMode] = value; }
}
}

2 responses to “Configuration and Settings Management

  1. Pingback: Configuration and Settings Management « Linguistic forms

  2. Pingback: Source code for settings management project « Linguistic forms

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s