Tom Hollander's blog

patterns, practices and pontification

External configuration files in Enterprise Library for .NET Framework 2.0

External configuration files in Enterprise Library for .NET Framework 2.0

  • Comments 26

I don't know if it's the onset of spring (or autumn, depending on your hemisphere), but judging from the number of mails I've received on this topic in the last few weeks, it seems that everyone is trying to make the Enterprise Library configuration system work with external files in one way or the other. So the time has come to explain a few approaches that you can use to do this.

First, I wrote a highly sophisticated and useful application that uses two blocks:

Program.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

namespace
EntLibConfigStuff
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Database db = DatabaseFactory.CreateDatabase("Tom's Connection");
                db.ExecuteNonQuery("This stored proc doesn't exist!");
            }
            catch (Exception ex)
            {
                if (ExceptionPolicy.HandleException(ex, "Tom's Policy"))
                    throw;
            }
        }
    }
}

Of course we want to configure the application blocks too, so I created an app.config file:

app.config

<?xml version="1.0" encoding="utf-8"?>
<
configuration>
    <
configSections>
        <
section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />
        <
section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </
configSections>
    <
exceptionHandling>
        <
exceptionPolicies>
            <
add name="Tom's Policy">
                <
exceptionTypes>
                    <
add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="ThrowNewException" name="Exception">
                        <
exceptionHandlers>
                            <
add exceptionMessage="Crikey!" replaceExceptionType="System.ApplicationException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"
name="Replace Handler" />
                        </
exceptionHandlers>
                    </
add>
                </
exceptionTypes>
            </
add>
        </
exceptionPolicies>
    </
exceptionHandling>
    <
connectionStrings>
        <
add name="Tom's Connection" connectionString="Database=EntLibQuickStarts;Server=(local)\SQLEXPRESS;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
    </
connectionStrings>
</
configuration>

So far, nothing very interesting here - this is the default way to use Enterprise Library. But what if you don’t want to store all of your configuration in app.config or web.config? Here are a few options:

Using a different ConfigurationSource

Enterprise Library for .NET Framework 2.0 uses an instance of a class implementing IConfigurationSource to retrieve configuration from the storage location. If you don’t do anything special, you’ll get a SystemConfigurationSource (this is what happened in the first example). However it’s possible to configure your application to use a different configuration source. Enterprise Library ships with a couple of alternatives: the FileConfigurationSource is a core part of the library, and we also include a simple SqlConfigurationSource as an example of how to create your own. Since this post is about using external files, we’re going to use a FileConfigurationSource here to tell Enterprise Library to read the configuration from an external file.

 

To do this, you need to add a Configuration Sources node to your application’s configuration using the Enterprise Library Configuration tool. From there you can add a File Configuration Source node and set the filename you wish to use. Finally, while you can configure as many Configuration Sources as you want using the tool, only one is ‘selected’ to be the one which Enterprise Library will automatically use if you don’t do anything special. Use the SelectedSource property under the Configuration Sources node to select which source you wish to use automatically (i.e. the FileConfigurationSource for this example).

 

When I saved my application’s configuration using the FileConfigurationSource, I now get two files: my app.config file, and the external file (I used external.config):

 

app.config

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <configSections>

    <section name="enterpriseLibrary.ConfigurationSource" type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationSourceSection, Microsoft.Practices.EnterpriseLibrary.Common, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />

  </configSections>

  <enterpriseLibrary.ConfigurationSource selectedSource="File Configuration Source">

    <sources>

      <add name="File Configuration Source" type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.FileConfigurationSource, Microsoft.Practices.EnterpriseLibrary.Common, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"

        filePath="external.config" />

    </sources>

  </enterpriseLibrary.ConfigurationSource>

</configuration>

 

external.config

<configuration>

    <configSections>

        <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />

        <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />

    </configSections>

    <exceptionHandling>

        <exceptionPolicies>

            <add name="Tom's Policy">

                <exceptionTypes>

                    <add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

                        postHandlingAction="ThrowNewException" name="Exception">

                        <exceptionHandlers>

                            <add exceptionMessage="Crikey!" replaceExceptionType="System.ApplicationException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

                                type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"

                                name="Replace Handler" />

                        </exceptionHandlers>

                    </add>

                </exceptionTypes>

            </add>

        </exceptionPolicies>

    </exceptionHandling>

    <connectionStrings>

        <add name="Tom's Connection" connectionString="Database=EntLibQuickStarts;Server=(local)\SQLEXPRESS;Integrated Security=SSPI;"

            providerName="System.Data.SqlClient" />

    </connectionStrings>

</configuration>

Using Several ConfigurationSources

There are a couple of things to note about the solution above. First, while the interesting configuration was indeed stored in a separate file, we still needed an app.config file for the “metaconfiguration” which tells Enterprise Library which Configuration Source to use. Second, both application blocks’ configuration was stored in the same file. These facts may or may not be an issue in your environment, but it’s good to know there are more options. For example, many people want configuration to be ‘owned’ by a DLL rather than by its host application, in which case you probably don’t want to impose any requirements on its app.config/web.config file at all.

 

While you can only have one Configuration Source defined in “metaconfiguration” or the app.config/web.config file, you can actually use as many different Configuration Sources as you want (one for each section, or one for each day of the week if you choose) – provided you’re prepared to do a little more work in your application code. Each application block has multiple entry points – normally you will probably use static factories or façades such as DatabaseFactory or ExceptionPolicy. However each block also provides instance-based entry points that give you more control over Configuration Sources (amongst other things) – such as DatabaseProviderFactory and ExceptionPolicyFactory. In this example we’ll make some minor changes to my phenomenal application to use the instance-based entry points so we can specify which Configuration Sources to use:

 

Program.cs (partial)

static void Main(string[] args)

{

    try

    {

        FileConfigurationSource dataSource = new FileConfigurationSource("data-filesource.config");

                        DatabaseProviderFactory dbFactory = new DatabaseProviderFactory(dataSource);

        Database db = dbFactory.Create("Tom's Connection");

        db.ExecuteNonQuery("This stored proc doesn't exist!");

    }

    catch (Exception ex)

    {

        FileConfigurationSource exceptionsSource = new FileConfigurationSource("exceptions-filesource.config");

        ExceptionPolicyFactory exceptionFactory = new ExceptionPolicyFactory(exceptionsSource);

        ExceptionPolicyImpl exceptionPolicy = exceptionFactory.Create("Tom's Policy");

        if (exceptionPolicy.HandleException(ex))

            throw;

    }

}

 

data-filesource.config

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <configSections>

      <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />

  </configSections>

 

  <connectionStrings>

    <add name="Tom's Connection" connectionString="Database=EntLibQuickStarts;Server=(local)\SQLEXPRESS;Integrated Security=SSPI;"

      providerName="System.Data.SqlClient" />

  </connectionStrings>

</configuration>

 

exceptions.filesource.config

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <configSections>

    <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />

     </configSections>

  <exceptionHandling>

    <exceptionPolicies>

      <add name="Tom's Policy">

        <exceptionTypes>

          <add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

            postHandlingAction="ThrowNewException" name="Exception">

            <exceptionHandlers>

              <add exceptionMessage="Crikey!" replaceExceptionType="System.ApplicationException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

                type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"

                name="Replace Handler" />

            </exceptionHandlers>

          </add>

        </exceptionTypes>

      </add>

    </exceptionPolicies>

  </exceptionHandling>

</configuration>

 

One thing to note about this solution: the configuration tool only works if you have an app.config to load, but in this solution we don’t actually have an app.config. While you could go an edit the configuration files by hand, a better approach is to create a ‘dummy’ app.config file for each Configuration Source you wish to use. You don’t need to deploy these files with the application, but you can open these with the tool to edit the configuration much less painlessly.

Using .NET’s configSource attribute

Amazingly enough I’m still not done here. There is one more option available which in many ways comes the closest to emulating the way that Enterprise Library for .NET Framework 1.1 worked – and it’s not even an Enterprise Library feature!

 

System.Configuration in .NET Framework 2.0 comes with a new (and not particularly well documented) feature that lets you store any configuration section in an external configuration file (although you do still need an app.config/web.config file to declare the sections). This feature is driven by an attribute which is confusingly enough called configSource, although it’s got nothing to do with the Enterprise Library Configuration Source class.

 

One catch about this solution – while it works great at runtime, the Enterprise Library Configuration tool doesn’t understand the configSource attribute. While you can successfully load configuration files which use this attribute, when you save the file using the tool it will put everything back in your app.config/web.config file. So if you want to go down this path, you’ll need to do a bit of XML manipulation by hand.

 

This solution uses the original Program.cs shown at the top of the article, and the following three config files:

 

app.config

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <configSections>

    <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />

    <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />

  </configSections>

  <exceptionHandling configSource="exceptions.config" />

  <connectionStrings configSource="data.config" />

</configuration>

 

data.config

<?xml version="1.0" encoding="utf-8"?>

  <connectionStrings>

    <add name="Tom's Connection" connectionString="Database=EntLibQuickStarts;Server=(local)\SQLEXPRESS;Integrated Security=SSPI;"

      providerName="System.Data.SqlClient" />

  </connectionStrings>

 

exceptions.config

<?xml version="1.0" encoding="utf-8"?>

  <exceptionHandling>

    <exceptionPolicies>

      <add name="Tom's Policy">

        <exceptionTypes>

          <add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

            postHandlingAction="ThrowNewException" name="Exception">

            <exceptionHandlers>

              <add exceptionMessage="Crikey!" replaceExceptionType="System.ApplicationException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

                type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"

                name="Replace Handler" />

            </exceptionHandlers>

          </add>

        </exceptionTypes>

      </add>

    </exceptionPolicies>

  </exceptionHandling>

 

So there you go – I hope you find that at least one of these solutions will work out for what you’re doing. Enjoy!

 

This posting is provided "AS IS" with no warranties, and confers no rights.

  • Thanks.  I was using the configSource method and dumped it due to not being able to use the Configuration tool.

    You are right about the configuration APIs not being very well documented.  Unfortunately, this goes for the EL as well.  I've watched the web cast on the ObjectBuilder and configuration but it only scratches the surface of the configuration possiblities.  I got lost in anything more than just a simple configuration and gave up.  I already had a solution using a ConfigurationSectionHandler and couldn’t' spare the time to learn the intricate details of ObjectBuilder to accommodate my scenario.  

    I understand the benefit but the cost/benefit ratio is way to high to devote more time.
  • Thanks for the feedback Mark. ObjectBuilder was a late addition to the scope of both EntLib and CAB and it isn't documented anywhere near as well as we'd have liked. Hopefully we'll be able to improve this in the future, but in the meantime you can post any questions or answers about ObjectBuilder at http://practices.gotdotnet.com/projects/objectbuilder

    Tom
  • Tom,

    One of the caveats of using the configSource attribute is that it has to be a physical path in the same directory or a sub-directory of the application root (as I understand it).  It's worth mentioning as this unfortunately eliminates, e.g., sharing config files over a UNC share.
  • Tom,

    We use the confiSource attribute as you have discussed in your last example but as the post above points out, the external configuration sources having to reside in the application directory path or subdirectories.  This is an unfortunate restriction on what otherwise is a pretty tidy solution.

    Can you recommend a simple wrapper that can be placed over System.Configuration that would allow configuration sources outside of the application directory path to be used?
  • Hi Tom,

    Is it possible to read the XML config files from a memory stream instead of file ? I would like to configure the caching block at runtime without using any local config file.

    Kind regards,
    Danny
  • Hi Tom,

    I just wanted to add that I have patched the SqlConfigurationSource to make it work with every ConfigurationSection derived class. This enables me to import via a GUI every App.Config (not only the Enterprise Library Configuration sections) into SQL Server:

    http://geekswithblogs.net/akraus1/articles/75391.aspx
  • Hi Tom:

    I see what you're saying about multiple config files for 1 app, but what about multiple config files for multiple apps?  I'm using an .exe that references another .dll, both being built from the same solution.  However, DAAB in the .dll is only seeing the .exe's connection strings?!  
    Mind you, this is in debug mode...I assume when deployed, the .dll will look for the config file with it's own name, but I wonder how it's even working at all since the (copied) .dll that's actually being loaded is no longer in the directory with it's appropriate config file.
    I tried copying the dll's config file to where the .exe is running, but it won't even look at it! it seems to insist on loading the .exe's!!

    Is there no way to have multiple config files with multiple projects?  I'm sure I'm missing something! (I'm assuming the latter to be the case ;)

    Cheers
  • Alois - way cool! Thanks - yet again - for putting this together and contributing to the community!

    Eric - this is the default behavior with .NET and with Enterprise Library; configuration files are only associated with exe and web projects, not with DLLs. However by using a FileConfigurationSource with Enterprise Library you can associate the configuration with the DLL project. If you don't want to impose any requirements on the exe's configuration file, follow the example about "using several configuration sources" above (even if you only want to use one).

    Tom
  • If i use configSource method, "you’ll need to do a bit of XML manipulation by hand" , any idea on how to write back the the external config file instead of write to the app.config?

    thanks
  • Good stuff!
  • Kian: The tool won't support this - it will always write the data back to the app.config file. If you want to use the configSource technique, you'll need to add this attribute by hand and move the code from app.config to the external file.

    Tom
  • Thanks Tom,
    But may i know what attribute you mean for? is it those XML Attribute?

    thanks again

  • Sorry for not clarify what i intend to do. I will like to store my custom object into my custom configuration file. And i will use own configuration tool to save the configured object.

    thanks
  • So is it possible to store my own application settings in a file located on a unc share using the file configuration source, or is that only to store the Ent Lib application block's configuration?
Page 1 of 2 (26 items) 12