Recently I have been working on ways to solve configuration issues in large, multi environment solutions. In the beginning, I simply wanted to store shared app settings and connection strings with a class library so I didn't have to keep copying common configuration settings from project to project within the same solution. Taking that a step further, I thought it would be great to auto detect the runtime environment and use the right app settings and connection strings from that shared configuration file. This all works great, but it has two major drawbacks: firstly, third party tools such as Elmah, and built in tools such as the Membership, Profile and Role Providers look no further than the built in
ConfigurationManager object for appSettings and connection strings which forces us to subclass (Dynamically setting the Elmah connection string at runtime) or override their initialization (Setting Membership-Profile-Role provider's connection string at runtime) in order for them to work with our new settings. Not all third party tools will be as easy to fix. Secondly, all the developers working on the project must be trained to use the new techniques and always remember to use
Core.Configuration.AppSettings["key"] instead of
ConfigurationManager.AppSettings["key"] may be
null or hold the wrong value.
With that in mind, the next logical step was to find a way to override the built in
ConfigurationManager ensuring that the
Core.Configuration settings are fully integrated. In short: any call to
ConfigurationManager.ConnectionStrings should return the correct setting, whether that setting comes from the local web/app.config or the Core.Config. In order to do this, it is assumed that if a setting appears both in the local app/web.config and the Core.Config files, then the value in the Core.Config file will be the value returned.
Download the latest version of the
Williablog.Core project: Williablog.Core.zip (110.11 kB).
Add a reference to it from your project (either to the project or the DLL in the bin folder) and the first line in
void Main() of your console Application or (if a web application)
Application_Start() in Global.asax should be:
This will reinitialize the
Configuration forcing it to rebuild the static cache of values but this time we are in control, and as a result we are able to effectively override the
ConfigurationManager. Here is the code:
public sealed class ConfigSystem : IInternalConfigSystem
private static IInternalConfigSystem clientConfigSystem;
private object appsettings;
private object connectionStrings;
The code to actually merge our collections is implemented as Extension methods:
public static class IEnumerableExtensions
If we create a console application to test with, complete with its own app.config file that looks like this:
<add key="WebServiceUrl" value="http://webservices.yourserver.com/YourService.asmx"/>
<add key="SmtpServer" value="smtp.yourmailserver.com"/>
<add key="LocalOnly" value="This is from the local app.config"/>
<add name="AppData" connectionString="data source=Audi01;
initial catalog=MyDB;User ID=User;Password=Password;"
<add name="ElmahDB" connectionString="Database=ELMAH;
And run it with the following code:
static void Main(string args)
The output is:
This is from the local app.config
data source=Ford01;initial catalog=MyDB;User ID=User;Password=Password;
With the exception of the middle one (
LocalOnly), all of these settings come from Williablog.Core.Config, not the local app.config proving that the config files were successfully merged.
ConfigSystem class could be modified to retrieve the additional appsettings from the registry, from a database or from any other source you care to use.
I'd like to thank the contributers/authors of the following articles which I found very helpful: