Config files for DLLs

kick it on DotNetKicks.com

Every once in a while I'll write a DLL and wish I could easily externally configure settings for it like you one do with web.config files and ConfigurationManager in ASP.NET applications or with .exe.config files. The idea is to avoid hardcoding settings in the DLL and instead put them in a XML file where they can later be changed as needed.

I finally got around to doing something about not having this same functionality for class libraries. Now, with AssemblyConfig, you can access settings from a .config file for your DLLs just like you can with ASP.NET.

Config File Format

AssemblyConfig looks for config files that match the name of the assembly. If your assembly is Image.dll, create a config file called Image.dll.config. Use the same XML structure that you'd use with an ASP.NET application or .exe.configs:

<configuration>
	<appSettings>
		<add key="setting" vaue="settingValue" />
	</appSettings>
</configuration>

Retrieving Settings

Accessing settings is just as easy as with ConfigurationManager. You can either access settings by key:

string value = AssemblyConfig.Setting("key");

Or you can access settings via XPaths:

string copyright = AssemblyConfig.Setting("/configuration/site/copyright");

AssemblyConfig even lets you use generics to type the return value of a setting if the type you specify implements IConvertible*:

int cacheTimeout = AssemblyConfig.Setting<int>("CacheTimeOutInSecs");
DateTime copyStart = AssemblyConfig.Setting<DateTime>("/configuration/site/copyrightStart");

*Types that implement IConvertible are: Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char, and String.

One last thing AssemblyConfig lets you do is access the entire config file using LINQ by returning the config's root XElement. This can be especially useful if you include extra data in your config files besides just <add ...> elements. You could create a config file like this:

<configuration>
	<appSettings>
		<add key="setting" vaue="settingValue" />
	</appSettings>
	<people>
		<person>
			<name...
		</person>
	</people>
</configuration>

The people data can then be accessed using the Root property of AssemblyConfig:

var people = AssemblyConfig.Root.Descendants("person");

Implementing AssemblyConfig

To use AssemblyConfig, add a reference to AssemblyConfig.dll in your project and call AssemblyConfig.Setting(), AssemblyConfig.Setting<T>(), or AssemblyConfig.Root as demonstrated from within your DLL class library.

Be on the lookout for a few exceptions when using AssemblyConfig. Instead of silently failing, like ConfigurationManager does by returning null if a setting is missing, AssemblyConfig will throw a SettingNotFoundException. This is preferable because a missing setting should be an unrecoverable scenario. A few other exceptions (all, including SettingNotFoundException, deriving from AssemblyConfigException) to look out for are:

  • DuplicateSettingException—if there are two settings with the same key or more than one element at a specified XPath
  • ConfigFileMissingException—if an attempt is made to retrieve a setting and there is no config file
  • ConfigFileMalformedException—if the config file is not a valid XML document or is missing the <configuration> root

Pretty straight forward, no? It's a small and simple enough tool that I haven't created an open source project out of it, but if you have any good suggestions, I'd love to implement them and update the post here to reflect any changes.

kick it on DotNetKicks.com

Tags:

11 Responses to “Config files for DLLs”

  1. JMP Says:
    November 15th, 2009 at 8:51 am

    Why would I do this? Why not keep the configuration stuff in the applications config file and then pass it when I initialize.

    I guess what I’m saying is, what are the benefits of doing this? What situations do you foresee that make this a requirement?

  2. CRG Says:
    November 15th, 2009 at 9:53 am

    I’m not sure how it is in VC#, but in VB, you could use the settings designer to create a settings file for a dll.

    JMP, one usage scenario, plug-ins.

  3. Chris Says:
    November 15th, 2009 at 2:43 pm

    @JMP: I’ve worked on many projects where a DLL was used across multiple, even tens, of applications. It’s a lot easier to copy a DLL along with its .config file to be used with an application than to change every application’s .config that uses that DLL.

    Plus, we have separate assemblies to keep separations of concerns with code. You create a DLL instead of adding the code to an application so that it can be plugged into an application. Just like keeping that DLL code separate from an application’s codebase is a good idea, so is keeping settings that only apply to that DLL, don’t you think?

  4. Yngve Says:
    November 16th, 2009 at 1:31 am

    So how would this work if you have the assembly added to GAC?

  5. Chris Says:
    November 16th, 2009 at 10:36 pm

    @Yngve: in that case, you’re just out of luck. Or, do you think having the .config file for the DLL in the same directory as the .exe or application referencing the DLL would be a good idea? Have any suggestions?

  6. phlyingpenguin Says:
    November 18th, 2009 at 4:12 pm

    This sounds like a very MS.NET centric view of the world. If you need one configuration to be passed into a library then it sounds to me like you need central config files instead of per application or (GAH!) per library configs. Think UNIX. Even if a separate library is the party parsing the config file and the program is only optionally passing in options, this allows much more control and makes much more sense in terms of reusing the library. Libraries should be reused and be able to be recycled in ANY configuration situation, with or without a config file.

  7. Chris Winberry Says:
    November 18th, 2009 at 4:36 pm

    @Chris: regarding assemblies in the GAC: I created a very similar solution to yours that is used by large group of developers across multiple libraries and apps that must live on the same system. The way I handled assemblies in the GAC was to create a “GlobalConfigDir” key in the machine config that points to a shared location where an assembly’s config may be found. The config loader then scanned for the config file in the following order (assuming Foo.dll v1.0.0.0):

    - [Current assembly location]/Foo.1.0.0.dll.config
    - [Current assembly location]/Foo.dll.config
    - [GlobalConfigDir]/Foo.1.0.0.dll.config
    - [GlobalConfigDir]/Foo.dll.config

    Care must be taken however, because in larger systems, there’s enough rope to hang one’s self (winding up with too many configs to track and update).

  8. Chris Says:
    November 18th, 2009 at 5:25 pm

    @PhlyingPenguin: Nice name :) . Anywho, centralized configurations are generally a good idea—just like variables, you don’t want five definitions of the same setting being five identical config files. That’s a recipe for disaster, surely. One option for storing settings, as you say, is an configuration file per application. .NET has built-in functionality for that. Another more flexible option might be putting settings in a database.

    There are times when it’s better to be more modular, however. A per-assembly config file definitely isn’t the best solution for all configuration needs, but it’s one solution to one particular problem.

    I’ll give you an example: at my last employer, we had four servers downloading images, all using the same DLL to help accomplish this. One of the settings in the DLL was where to cache the images. In this scenario, the local paths where the images were cached actually needed to be different on each server as a result of the servers’ hardware (physical drive sizes).

    The application using this DLL already had a largly bloated configuration file for itself, so separating out the concerns of the particular DLL into its own config file made changing settings related to image caching easier.

    The point of libraries, like you say, is to be modular and recyclable, and that’s exactly what decoupling the DLL settings from the application’s settings does. Is it always the best idea? No. Sometimes? Sure.

  9. Chris Says:
    November 18th, 2009 at 5:46 pm

    @Chris W: I like your idea. To be more useful, there definitely needs to be a way to load config files if an config’d assembly resides in the GAC. A global config location sounds great. Aside from checking the directory of the assembly that referencing the assembly with the config file, I don’t know what else one could do. Thanks for the suggestion!

  10. Marc Schubert Says:
    November 26th, 2009 at 2:58 pm

    Hi,

    each assembly (wheter is an DLL or an EXE) has the opportunity to store its configuratios in an app.config-File.
    Makes it sense ? It’s appropriate to your custom scneario, so i can’t give an generic answer.

    An easy way to get the configs of your Assembly is by using System.Reflection.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Configuration;
    using System.Reflection;

    namespace Marc.MyLibrary
    {
    public class MySampleClass
    {
    public static string DoSomething()
    {
    // Preferences: Store an App-Config beside your Assembly (DLL or EXE)
    // named as a regular app.config
    // example:
    // Marc.MyLibrary.dll
    // Marc.MyLibrary.dll.config

    // 1) Get the Location of your Assembly

    string path = typeof(MySampleClass).Assembly.Location;

    // 2) Use the path to get an Configuration-Object via ConfigurationManager
    // (Info: Please set a reference to System.Configuration)

    Configuration config = ConfigurationManager.OpenExeConfiguration(path);

    // 3) Access via Indexer on your defined key
    string myValue = config.AppSettings.Settings["MyKey"].Value;

    return myValue;
    }

    }
    }

    Regards,

    Marc

  11. Denisha Sangasy Says:
    January 8th, 2010 at 8:17 am

    Interessanter Beitrag, vielen Dank.

Leave a Reply

To include code, use <pre lang="csharp">// code...</pre>, where "csharp" is any language (e.g. "javascript", "html", "xml", etc).