Keep your .config clean with external config files

June 16, 2008 at 1:25 PMAndre Loker

The web.config (or app.config for non-web applications) file is the central place to configure your web application, starting from connection strings, over application settings to ASP.NET specific topics like caching, authentication & authorization, sessions as well as HTTP handlers and modules. Normally the web.config starts as a neat little pet you can easily manage. But as soon as your project grows mature, web.config turns out to be a huge beast. The size itself is often not that much of a problem. An issue I personally find more important is the fact that I need my web.config files to be different for my developer's machine and the deployment scenarios: on my dev machine I certainly have different connection strings than on the production server, the same counts for app settings, logging, compilation settings etc. pp.

Maintaining multiple configuration files is a PITA, especially when it comes to the configuration parts that have to be similar on all config files (e.g. http handlers and modules will most likely be the same).

Luckily the .NET configuration system has a feature that drastically lightens the burden for us. Each configuration section may define an attribute named "configSource" to define an alternate location from where the configuration has to be loaded. Let's have a look at an example.

In web.config locate or create the connectionStrings section. Remove all child elements (<add>, <remove> or <clear>) and add a new attribute named configSource. Give it the name of a file called "ConnectionStrings.config" as its value:

   1: <connectionStrings configSource="ConnectionStrings.config"/>

Add a new file called "ConnectionStrings.config" to the same directory where web.config lives and define its content as follows:

   1: <?xml version="1.0"?>
   2: <connectionStrings>
   3:   <add name="MyDB" connectionString="Data Source=myServerAddress;Initial Catalog=MyDataBase;Integrated Security=SSPI;"/>
   4: </connectionStrings>

That's it. Now the content of ConnectionStrings.config will be used for the connectionStrings section.

Some things to note:

  • The external configuration file must contain the section's name as the root element, i.e. an external config file for the <smtp> element must have the <smtp> node as it's root even though the element is nested within system.net/mailSettings in the original web.config.
  • If you use configSource you must not define any other attribute or child element for the respective section in web.config. This means that configSource can never be used to add additional content to a section. It will always replace the section.
  • It might be useful to move configuration files to a subfolder to keep the root directory clean. I personally use a subfolder named Config. The configSource attributes have to include the folder in that case, like configSource="Config\connectionStrings.config"
  • The actual file extension of the external configuration files does not matter (as long as you point to the correct file in the configSource attribute). Keep in mind, though, that depending on the extension the web server might or might not serve the literal content of the config files when requested. If the config files contain sensitive information (DB passwords etc.) this can be a massive security risk. .config is one of the extensions IIS/ASP.NET will refuse serve to the client. Therefore I recommend to always give configuration files the extension ".config".

Multiple deployment scenarios

Now that we are able to move configuration settings to an external source file the web.config becomes easier to manage: if for example we need to have two different sets of connection strings (or app settings etc.) for different deployment scenarios we can define multiple external configuration files, each one for a different scenario, like: connectionStrings-dev.config for my dev machine and connectionStrings-deploy.config for the production server. All I have to do to switch between these two is to modify the configSource in the web.config. (BTW: the web deployment projects can do this automatically for you, just check the "Enable Web.config file section replacement" checkbox under the "Deployment" tab and define the new file names; see Extreme ASP.NET: Web Deployment Projects under "Pluggable Configuration Files").

Hiding sensitive information

If you're working on an open source project that uses a shared code repository you may not want specific information to appear in the repository, like database connection strings, smtp credentials. If you move those settings to external configuration files it is easy to exclude them from your repository.

Non-standard config sections

ConfigSource is a feature of the .NET configuration system and not specific to the ASP.NET configuration sections. Therefore it should work with other config sections as well.

Recommendations

Personally I recommend to move the following sections to external configuration files:

  • appSettings - likely to change for different deployment scenarios
  • connectionStrings - likely to change for different deployment scenarios
  • smtp in system.net/mailSettings - for security reasons and because it's likely to change for different deployment scenarios
  • machineKey in system.web - primarily for security reasons
  • External libraries like log4net, Castle Windsor, NHibernate etc

Posted in: ASP.NET

Tags: , ,

Comments (23) -

Alex Dresko
United States Alex Dresko says:

Does editing one of these external configuration files reset the web application like it does when you edit and save the web.config?

Andre Loker
Germany Andre Loker says:

Hi Alex,

Whether the app restarts or not on changes depends on the restartOnExternalChanges attribute of the sections element for the respective configuration section: msdn.microsoft.com/en-us/library/ms228245.aspx

The default behaviour is to restart the application, though it can be overriden with the attribute mentioned above.

Some sections already override this by default, such as appSettings (meaning that changes to an external appSettings file won't restart the app). Look at your machine.config to find out about other sections for which this applies.

Regards,
Andre

Samuel Jack
United Kingdom Samuel Jack says:

Thanks for blogging this. It came just at the right time!

My last project used this quite extensively.  It's a great approach, but it can get confusing when you have 30 different configuration files.

asp.net scripts
asp.net scripts says:

great timing I found your article. Just fighting with a multi deployment case and I had forgot about external config files. Was almost thinking of putting the keys needing differnt values into some sort of ini file instead and read it myself...thanks again

Everybody has posted this solution, but nobody tells you how to use the external connection string from code!  

Andre Loker
Germany Andre Loker says:

@chabber:
The usage does not change. For the code it does not matter whether the connection strings are stored in web.config or an external code file. To get the connection string named "MyDatabase" you would do like:

string conStr = ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString;

Thanks for this!

Can we have child config files to web.config file like a tree structure in Visual Studio. Similar to the aspx file with aspx.cs & aspx.designer.cs.
If yes how can we acheive this.

Not as far as I know. I tend to put all external config files into a separate folder.

"The external configuration file must contain the section's name as the root element"

Isn't it possible to somehow work around this...? It would solve a lot of problems for me... Smile

Hi! I want to specify external config for the compilation section. Like this

<compilation  configSource="Config\Compilation.config" />

Compilaction.config looks like this:
<?xml version="1.0"?>
  <compilation debug="true">
      <assemblies>
          <add assembly="System.Management, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>.
           ...
       </assemblies>
  </compilation>

But it doesn't work... When I start to debug I get this message "The page cannot be run in debug mode because debugging is not enabled in Web.config file"

I have VS 2005 + XP Pro.

This looks exactly the same as this issue (but I'm running VS 2005):
connect.microsoft.com/.../ViewFeedback.aspx


Any help...  

Thx for this useful post

How would you create an external file for an app.config (not web.config) for .net 2008
for the Application settings section. I am using vb.net 2008 for a windows app.  All the samples on this problem use web.config not app.config or it is done in another version of .net.

In my app.config I have the following:

<applicationSettings>
        <FIOES.My.MySettings>
             <setting name="MainImage" serializeAs="String">
                <value>Default</value>
            </setting>
        </FIOES.My.MySettings>
    </applicationSettings>

I need to put this in an external config file, but I am getting an error.  Here is what I have done in the app.config.  I substitute the above with :

   <applicationSettings configSource="myappext.config"/>

and in myappext.config I have:

<?xml version="1.0" encoding="utf-8" ?>
  <applicationSettings>
        <FIOES.My.MySettings>
            <setting name="ImageOptions" serializeAs="String">
                       <setting name="MainImage" serializeAs="String">
                <value>Default</value>
            </setting>
        </FIOES.My.MySettings>
    </applicationSettings>

I put this in the same directory as the app.config and in the bin of the  project but I receive an error that the app.config cannot be read.

NOTE:  I have done this successfully for my connection strings and I am doing the same thing, but I guess it has to be different for application settings.


I am wondering if I need to  also make changes to the following:


<configuration>
<configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral,      PublicKeyToken=b77a5c561934e089" >
   <section name="FIOES.My.MySettings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

requirePermission="false" />
  </sectionGroup>
</configSections>

This is required in the app.config  for the application and user settings and in this way is different from the connection string.  However, I keep getting an error that the config file has failed to initialize.


What do I need to do?

@smhaig
If I remember correctly you can only move config sections to external files, not config section groups.

If I try this it works as expected:
app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>  
  <configSections>
    <sectionGroup name="applicationSettings" ... >
      <section name="FIOES.My.MySettings" .../>
    </sectionGroup>
  </configSections>

  <applicationSettings>
    <FIOES.My.MySettings configSource="myappext.config"/>
  </applicationSettings>
</configuration>

myappext.config
<?xml version="1.0" encoding="utf-8" ?>
<FIOES.My.MySettings >
  <setting name="MainImage" serializeAs="String">
    <value>Default</value>
  </setting>
</FIOES.My.MySettings>

program.cs
using System;
using System.Configuration;

class Program {
  static void Main() {
    var section = "applicationSettings/FIOES.My.MySettings";
    var settings = (ClientSettingsSection) ConfigurationManager.GetSection(section);
    foreach(SettingElement o in settings.Settings) {
      Console.WriteLine(o.Name);
    }
  }
}

@Vladimir
Sorry for the late response. This issue seems to be fixed in VS2008 SP1, it works smoothly here. But I'm afraid it doesn't change the situation for VS2005 :-(

I am trying to do

<system.serviceModel>

    <bindings configSource="Bindings.config">
    </bindings>
</system.serviceModel>

but it says , Unable to open configSource file 'Bindings.config'.

Did you put Bindings.config in the same folder as web.config?

I think I'll go read some more of your blog posts!

I like how you write.Are you interesting in a part time writer job?

This is new info for me thanks.

Yes, I agree with you.
some times when we working with some spesific information we must keep it securely we not want this information to appear in the repository, and database connection strings is one of them.

thanks for your great information.

Is it possible to create a virtual directory in IIS that points to a network share and place your external config files in it?  For example:
IIS Virtual Dir "Config" points to \\WebFarm\MySite\Config
which contains connStrings.config
Then update web.config to use:
<connectionStrings configSource="Config\connStrings.config"/>

Pingbacks and trackbacks (2)+