Freigeben über


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.

Comments

  • Anonymous
    April 03, 2006
    The comment has been removed

  • Anonymous
    April 03, 2006
    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

  • Anonymous
    April 15, 2006
    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.

  • Anonymous
    April 17, 2006
    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?

  • Anonymous
    April 18, 2006
    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

  • Anonymous
    April 18, 2006
    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

  • Anonymous
    April 18, 2006
    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

  • Anonymous
    April 19, 2006
    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

  • Anonymous
    April 20, 2006
    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

  • Anonymous
    April 20, 2006
    Good stuff!

  • Anonymous
    April 21, 2006
    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

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

    thanks again

  • Anonymous
    April 23, 2006
    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

  • Anonymous
    April 23, 2006
    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?

  • Anonymous
    April 23, 2006
    Is there anyway to load you configuration without having to derive a class of ConfigurationSection.  configSource on relative path isn't that great!.

    I assumed Entlib would support IConfigurationSectionHandler in 2.0?

    IConfigurationSource source =
                       ConfigurationSourceFactory.Create("MyFileSection");

                   MyConfigurationSection section =
                       source.GetSection("Persistence") as MyConfigurationSection;

  • Anonymous
    April 24, 2006
    Hi Gerry,

    the Enteprise Library shows the best practices for the NET 2.0 platform which includes not to use any deprecated interfaces. As you might have noticed the IConfigurationSource interface has been deprecated with .NET 2.0 and should therefore not be used in new applications.

    Yours,
     Alois Kraus

  • Anonymous
    April 24, 2006
    I assume you mean IConfigurationSectionHandler is deprecated in 2.0 and not IConfigurationSource, as FileConfigurationSource has the interface IConfigurationSource in EntLib 2.0.  Mixed views on whether or not 'IConfigurationSectionHandler' is actually deprecated in 2.0, it isn't obselete and different web pages give support for it in 2.0!  I want to load my configuration using Tom's distributed files as above which they all return a ConfigurationSection element and not Object as with IConfigurationSectionHandler.  configSource limits development to relative path, this isn't good but using 'File Configuration Source' section to define external file to load configuration from (filePath="") is fantastic but these objects must return a typeof(ConfigurationSection).

    Do I really want to define my xml configuration using attribute based programming, ConfigurationSection, ConfigurationElement, ConfigurationElementCollection,etc and define ConfigurationProperty attributes of class getters/setters, it works a treat but the above distributed config loading, mixed with standard type deserialization is a much better way and less configuration especially when implementing IXmlSerializable.  At less I could use xsd.exe type generation.

    I came up with this below to bypass defining classes as ConfigurationElements, ConfigurationElementCollection and implemented IXmlSerializable or xsd type generation which gives me the class using configurationSection without all the work and also the above do not support generic types and your collections are just an array list under the hood, where my requirement is to be more specfic and to hold dictionary<String, class>.  Making these support generics <T> would have been good.  Obviously you can place you own instance of Dictionary and write/read from it overriding the base implementation.

    This below will let me use 2.0 way to load configuration, returning a derived class of ConfigurationSection, deserializing my object as I wish and providing a method to retrieve the internal deserialized class.  It obviously ignores the new way kinda.

    public class StatementConfigSection : ConfigurationSection
       {
           private MyClass _myClass;

           public StatementConfigSection()
               : base()
           {
           }

           protected override void DeserializeSection(System.Xml.XmlReader reader)
           {
               XmlSerializer serialiser = new XmlSerializer(typeof(MyClass));
               _myClass = serialiser.Deserialize(reader) as MyClass;
           }
          public MyClass GetMyClass()
          {
    return _myClass;
          }
    }

    I also didn't like the way it spat out the 'Add' as below.
    <Names>
    <Add forename =""/>
    </Names>

    Below is much better, I assume you can override the Serialise somewhere to write out as below?  If you know how that would be cool!
    <Names>
    <Name forename=""/>
    </Names>

  • Anonymous
    April 25, 2006
    Thanks Tom, this is good stuff. I feel I'm a lot closer to my solution but not quite there yet.

    Here's my problem:

    I have application x.exe. It calls a class library y.dll.

    y.dll needs access to config data. Its not appropriate to put it in the exe's config since (a) logically, the type of data doesn't fit, and (b) practically, y.dll could be used by any executable which needed the functionality.

    So I need at runtime for code in y.dll to run to y.dll.config for its configuration data.

    You seem to have a scenario above that fits this, but I'd like to add a twist - I'd like to do it without using the Enterprise Library classes (just so I have less to worry about deploying). Possible?

    Sorry if this seems to be a dumb question but I can find very little documentation out there for what (until I started using .net) I would have thought was a pretty trivial problem.

  • Anonymous
    April 25, 2006
    Peter - the examples that use the FileConfigurationSource should help in your scenario. If you decide not to deploy the EntLib DLLs, you can still copy the approach used in the FileConfigurationSource. The key part is the call to ConfigurationManager.OpenMappedExeConfiguration (which despite its name, will open any configuration file, not just one for an .exe).

    Hope this helps
    Tom

  • Anonymous
    April 25, 2006
    I tried using this approach with a CAB app and a WSE 3.0 app (the QuickStarts) and I assume they failed since these App Blocks don't use IConfigurationSource. Are there plans to incorporate the use of IConfigurationSource across the board so this approach can be used consistently with the Ent Lib infrastructure?

  • Anonymous
    April 26, 2006
    My last post may not have been clear, I refer to attempting to using FileConfigurationSource to put config info in a file other than app.config. It appears that the CompositeUI library & WebServicesConfiguration class don't use IConfigurationSource, so all config data must reside in app.config.

  • Anonymous
    May 05, 2007
    Read/Write App.config with .NET 2.0/Enterprise Library

  • Anonymous
    September 20, 2007
    One of the nice things about the Enterprise Library Logging Application Block (LAB) is that it's so darned

  • Anonymous
    February 03, 2008
    如何使用其他独立的配置文件来拆分Web.config/App.config, 如何设置重启选项等 (涉及.Net framework System.Configuration