Enterprise Library : Configuration Part 1.1 – Cofiguration Runtime Overview


In my last post I tried to give a brief overview of Configuration.  In this post I am going to go over the concepts of the Configuration Library.  The following components that make up the public interface for the runtime : the Configuration Manager, the Configuration Context, Configuration Providers, Storage Providers, Transformers and Xml Include Types.  These concepts let us separate the concerns and responsibilities of configuration.

Configuration Manager
The Configuration Manager is just a facade over the entire Configuration Runtime.  I would say 90% of the time you will use this functionality and not need anymore.  It is a static instance that allows you to work with configuration data, while hiding the implementation of how the data is retrieved.  This is similar to using ConfigurationSettings.GetConfig(). When working with the Configuration Manager,  realize that it works with your defined application configuration file.

Configuration Providers
A configuration provider is a contract for anyone that wants to provide a service that relies on configuration data.  Now in retrospect, this is really a configuration consumer.  In other words, the provider consumes configuration data and acts upon that data.  Given the Provider Model coming out in Whidbey, we opted for provider.  Sorry Brian.  The configuration provider contract is the interface IConfigurationProvider.  When creating a provider, use the ConfigurationProvider base class because it has implementation that will help you not have to write as much code

The heart of the Configuration Provider interface is the Initialize method.  This method excepts a ConfigurationView instance.  This object is just a wrapper on accessing configuration data.  Think of this as your data access API.  You may have heard that I rewrote Configuration a few times , and this was one of the last changes that we made for two reasons.  First and foremost we wanted each block to be able to respond to storage changes.  By hiding the data retrieval behind another level of indirection, you will be able to access the latest greatest data without worrying about responding to any events.  The second reason we did this is because Brian wanted this .   Actually it has to do with active versus passive data in our code.  Before, configuration data access was littered throughout the code which made it very active throughout.  This smelled really bad.  So we went to a more passive mode of accessing the data, and we expect this to get better in V2. 

Now for those interested, here are the gory details of how we get your application to respond to configuration changes.  When you create a Storage Provider, you use an IStorageProviderReader/Writer that implements an IConfigurationChangeWatcherFactory (more on this in a minute).  This allows you to create an IConfigurationChangeWatcher that will tell the Configuration Runtime when an external change happens. Since the Configuration Runtime caches live references to your data, if you store the data in your component, then you want get changes to that reference because we replace the reference in the cache with the new data.  You can see where you would not get what you expect

Storage Providers
The storage providers are responsible for reading and writing data to and from a physical storage. The interfaces for dealing with storage providers are the following : IStorageProviderReader, IStorageProviderWriter, IConfigurationChangeWatcherFactory, and IConfigurationChangeWatcher.

An IStorageProviderReader / Writer does exactly what they advertise, read and write configuration data. We separated out the reader and writer so you could have read only data.  There is no magic here, just your implementation.  We ship with a storage provider that reads and writes data to an external Xml file.  

An IStorageProviderReader implements the IConfigurationChangeWatcherFactory.  This allows us to query your provider for an IConfigurationChangeWatcher that will tell us when your external configuration data changes (of course you don’t have to have this).  We also have one for our own meta-data.  We ship with a watcher that watches files.  So if you want to create a storage provider for Sql Server you would have to write some mechanism to notify you that data has changed (like a service) or wait for Yukon .

Transformers
A transformer can (I say can because you don’t have to have one) sit between your storage provider and your application code. The transformer takes your data from storage and “transforms” it into something consumable for your application and vice-versa for your storage provider.  Now since we don’t place any limitations on a Transformer, I could imagine someone creating a composite transformer that would then invoke many transformers for your configuration data. Since we return data in the form of and XmlNode from our storage provider we have a Transformer that uses the XmlSerializer to transformer this into individual object graphs defined for each block.

Xml Include Types
Since we use the XmlSerialzier for our Transformer, we needed a way to tell the serializer what types it could expect to see.  Now internally we can use the XmlIncludeAttribute on our base class, but we didn’t want you recompiling everything any time you wanted to extend the library (I will keep my opinion to myself about why you put an attribute on a base class instead of the derived).  So when you define the Xml Include Types, we feed this to the serializer so you your types are recognized.

Configuration Context
The context is not all that interesting , it is just an instance based version of the Configuration Manager. One of the major differences is that the Configuration Manager uses the application configuration file, while the context can either use a separate configuration file or a ConfigurationDictionary that defines your configuration from code.  We needed this instance so we could pass around the right configuration to each and every block that would be used.  For example, you don’t need configuration to be defined in storage, you could just new it up and put it into a ConfigurationDictionary,  or it could come from a separate file (as I stated previously).  If we were to read the configuration from the application configuration file every time, well, you would not get what you expect.  So when you invoke a block and that configuration is read, all subsequent calls down the chain will use that ConfigurationContext.

Now that is a pretty abstract description so lets use a real example. When using the Exception Handling block, you can use the Logging Application Block to log data to a sink, which in turn uses the Data Access Block to log your exception to a database.  Now you can see if we don’t pass the right context all the way down the chain,  the Data Access Block would not have a clue what database it should use. 

Since the Context was really not covered well in the documentation (sorry about that), I will be posting on how to use it in the future.

Next post… how to write a StorageProvider .  Same code time, same code channel. 

Now playing: Rage Against the MachineVietnow

Comments (20)

  1. Scott Densmore drills into the configuration block of EntLib

  2. Peter says:

    Sorry if this is slightly off topic, but am I missiing something, or is the Data Access Application Block JUST for Oracle, SQL server & DB2. What about OLEDB or ODBC???

  3. Doug Rohrer says:

    Scott et al:

    I’ve posted the first of two walkthroughs on the entLib configuration manager on my blog (http://weblogs.asp.net/drohrer/). The post is at http://weblogs.asp.net/drohrer/archive/2005/02/02/366015.aspx – take a look and let me know what you think.

  4. Brian says:

    I am trying to use the Data block and the Config block to configure my database connections, but it seems that everything is sensitive to the process it’s being run under for the config paths. If I run my project directly, everything works ok as long as dataConfiguration.config and myApp.exe.config are both copied to bindebug. However, when I point NUnit at the same project, the ConfigurationBuilder chokes because suddenly it is looking in the root path for these files (where the NUnit project is located) instead of bindebug. I assume this is because I am now running under the NUnit process and so the Config dll gets run from that thread and looks for stuff relative to it’s parent app location??? I can fix it manually if I know where to copy the files, but surely I shouldn’t have to manually copy those files around depending on how I’m running the code? I tried using the TestDriven.Net add-in to run my unit tests and it was looking in yet another location (this time in my temp folder) for the config files.

    This issue is causing me huge headaches and I really hope I’m just missing something.

  5. Hi Brian,

    You have to make sure you are building the DebugUnitTest target in Visual Studio and running the assemblies built to the output directories for that target. Debug does not have any tests built into them. I can run both Nunit and TestDriven.NET just fine. If you want you can email me directly and I can try and help you out.

  6. skeamy says:

    Hi,

    Im relatively new to .Net so I may be asking for something obvious. I like the idea of the Enterprise Library however I would like to centralise my configurations files (app.config, logging.configuration etc..) either for a group of dlls or by machine basis. We have a COM+ application which contains 30 dlls and if each one had to be configured it would be a pain. I tried using Machine.config (Maybe showing my lack of .NET Knowledge now) and it did exacly what I required for my components however when I tried to run the quickstart samples again they complained about duplicate configuration which I assume is because its trying to read the config from both app.config and machine.config – I would have expected it to look for an app.config and if there wasn’t one to use the machine.config – any thoughts, ideas would be appreciated…

  7. Fbiots says:

    I’m trying to figure out how to use the configuration block for user preference ‘config’ files for a large enterprise windows forms application.

    I can’t use the XmlFileStorageProvider, because that only looks in the same directory as the app config file. (The User’s ApplicationData directory should be used; anyway, the app base dir will not allow writes while running under least privileged user. (See <a href="http://www.peterprovost.org/archive/2005/01/28/2645.aspx">Peter‘s posting on this</a>)

    So does it make sense to write a custom storage provider (similar to XmlFilesStorageProvider) for managing user preferences? Is it better to take a different approach for user prefs (like <a href="http://msdn.microsoft.com/msdnmag/issues/04/07/CustomPreferences/default.aspx"&gt;.NET App Preferences</a>?) Or is this all just overkill for user preferences?

  8. Fbiots,

    Read my new post and hopefully this will help you out.

    scott

  9. Troglite says:

    Scott,

    I am attempting to research an error condition that I am experiencing with the Config Block in EntLib 1.0. I have an admin web page that I use to populate some config values. I then have a second web pages that pulls those values in order to perform some work. Works like a charm with and without encryption.

    BUT… everytime the app pool is restarted in IIS, the app block throws an error when attempting to read the config values. The error is "The section name ‘XXXX’ could not be found in the Xml file M:*XXXXconfig.xml" (I’ve XX’d out the information that is implementation specific). Re-populating the values using the adin web page resolves the error… until the app pool is restarted again.

    Have you seen this before? Any pointers on how I may eliminate this error?

    Thanks,

    ~Justin

  10. Justin,

    I have not seen this before, but if you can send me some mail, maybe we can figure this out. I can try and repro this here. (I am sure this was a test case :)). Mail me @ scottden@remove_this.microsoft.com

  11. Troglite says:

    Scott,

    I found the problem. It appears that the name of the config section is case sensitive (which makes sense since its XML). All of my code referenced "MYconfig", with one exception. When I called GetConfiguration I passed in a value of "MYConfig". Once I corrected the shift in case for this string value, the errors went away.

    Hopefully this posting will help anyone who makes this same mistake.

    Thanks for the great tutorial and your generous offer to review my code!

    Sincerely,

    ~Justin