Config files for DLLs
Posted by Chris | Filed under .NET, C#
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
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
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:
One last thing AssemblyConfig lets you do is access the entire config file using LINQ by returning the config's root
<configuration> <appSettings> <add key="setting" vaue="settingValue" /> </appSettings> <people> <person> <name... </person> </people> </configuration>
The people data can then be accessed using the
var people = AssemblyConfig.Root.Descendants("person");
Implementing AssemblyConfig
To use AssemblyConfig, add a reference to AssemblyConfig.dll in your project and call
Be on the lookout for a few exceptions when using AssemblyConfig. Instead of silently failing, like
DuplicateSettingException —if there are two settings with the same key or more than one element at a specified XPathConfigFileMissingException —if an attempt is made to retrieve a setting and there is no config fileConfigFileMalformedException —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.
Tags: web.config config file settings
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?
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.
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?
November 16th, 2009 at 1:31 am
So how would this work if you have the assembly added to GAC?
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?
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.
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).
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.
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!
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
January 8th, 2010 at 8:17 am
Interessanter Beitrag, vielen Dank.