Share via


External configuration files in Enterprise Library for .NET Framework 2.0

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.