Introduction
While working with Windows Forms and web form applications in .NET 1.1, we realize that we can easily load application settings from configuration files. .NET configuration architecture made it very easy to load these files and read them in the application at runtime. But there are times when we develop complex business components and those must have their own configuration related data. Since these library components are independent of applications in which they are loaded, it makes sense that these components must have their own ConfigurationManager
. This article explains how we can develop Configuration Manager for a DLL. I have got inspiration from .NET 2.0 configuration architecture and tried to make this manager class exactly the same as in Framework 2.0. Those readers who haven't used ConfigurationManager
and WebConfigurationManager
provided by .NET 2.0 are encouraged to read the background section of this article.
Background
Since structure of the code in this article revolves around the configuration architecture present in Framework 1.1 and 2.0, it's better to understand first how to read config files in both versions of the Framework.
Framework 1.1
<!---->
<configuration>
<configSections>
<section name="MyTempSection"
type="System.Configuration.SingleTagSectionHangler" />
<section name="CustomSection1"
type="System.Configuration.DictionarySectionHandler" />
</configSections>
<MyTempSection amount="7500" currency="USD" />
<customSection1>
<add key="name" value="Muhammad" />
<add key="lastName" value="Irfan" />
</customSection1>
</configuration>
//To read above configuration sections we can use the following code:
IDictionary section = System.Configuration.ConfigurationSettings.GetConfig
("MyTempSection") as IDictionary
String amount = section["amount"] ;
String currency = section["currency"];
IDictionary section1 = System.Configuration.ConfigurationSettings.GetConfig
("CustomSection1") as IDictionary;
String name = (string) section1"name"];
String lastName = (string) section1["lastName"]
For section type in app.config, I have used SingleTagSectionHandler
and DictionarySectionHandler
just to make the example clear. If you want to load config data into your own type, then you can declare a custom type by implementing IConfigurationSectionHandler
. SingleTagSectionHandler
specifies the configuration settings as key value pairs using XML Attributes and DictionarySectionHandler
allows the configuration information to be specified as name value pairs in XML nodes. In .NET 2.0 configuration architecture is much more simplified and enhanced. Framework 2.0 contains WebConfigurationManager
& ConfigurationManager
that are core to this architecture. ConnectionStrings
property has been added that retrieves ConnectionStringSection
. By using connnectionStringSection
, we can get connectionStringSettingsCollection
and enumerate all attributes for that section. The following code demonstrates how to use custom config sections.
Framework 2.0
<!---->
<configuration>
<configSections>
<sectionGroup name="MySectionGroup">
<section name="settings" type="[NameSpace].[Class_Name], [AssemblyName]" />
</sectionGroup>
</configSections>
<MySectionGroup>
<settings dbProvider="System.Data.SqlClient" dbName="SPR01" />
</MySectionGroup>
</configuration>
//Once again to read the above section we can use the following code
System.Configuration.Configuration webConfig =
System.Configuration.ConfigurationManager.OpenExeConfiguration
(ConfigurationUserLevel.PerUserRoamingAndLocal);
Test_Desktop_2005.MyCustomConfigurationHandler config =
(Test_Desktop_2005.MyCustomConfigurationHandler)webConfig.GetSection
("MySectionGroup/settings");
string dbProvider = config.DBProvider;
string dbName = config.DBName;
// Class that creates the configuration handler
public class MyCustomConfigurationHandler : ConfigurationSection
{
// Empty Construct
public MyCustomConfigurationHandler() { }
// Loaded Construct
public MyCustomConfigurationHandler(string dbProvider, string dbName)
{
DBProvider = dbProvider;
DBName = dbName;
}
// First Number Property
[System.Configuration.ConfigurationProperty("dbProvider",
DefaultValue = "", IsRequired = true, IsDefaultCollection = false)]
public string DBProvider
{
get
{
return (string)this["dbProvider"];
}
set
{
this["dbProvider"] = value;
}
}
// Second Number Property
[System.Configuration.ConfigurationProperty("dbName",
DefaultValue = "", IsRequired = true, IsDefaultCollection = false)]
public string DBName
{
get
{
return (string)this["dbName"];
}
set
{
this["dbName"] = value;
}
}
}
Using the Code
Attached is the demo project which shows how to use the ConfigurationManager
class efficiently. We will use an XML file Configuration.xml as a configuration file. In the project, you can browse and locate the file. If you have a look at the code, you will come to know that using our ConfigurationManager
is the same as using ConfigurationManager
in .NET 2.0. It contains methods with the same name and parameters. For loading XML file, I have used XPathNavigator
because it doesn't build a node tree like XmlDocument
and XmlNode
; rather it only looks at one node at a time. It builds the nodes when you move to them. It doesn't use as much memory because it doesn't create the entire node tree in the beginning. It creates the tree dynamically as you move through the file.
<!---->
<configuration>
<cofigSection>
<globalThreshold amount="7500" currency="7500" />
<general>
<add key="key1" value="value1" />
<add key="key2" value="value2" />
<add key="key3" value="value3" />
<add key="key4" value="value4" />
</general>
</cofigSection>
<connectionStrings>
<add name="connection1" connectionString="connection_string_for_connection_1"
provider="System.Data.SqlClient" />
<add name="connection2" connectionString="connection_string_for_connection_2"
provider="System.Data.Oledb" />
</connectionStrings>
<appSettings>
<add key="WindowState" value="Maximized" />
</appSettings>
</configuration>
ConfigurationManager
has one pubic
method, i.e. OpenExeConfiguration
. It takes the config file path and returns configuration
object.
ConfigurationManager manager = new ConfigurationManager();
Configuration configuration = manager.OpenExeConfiguration
(this.openFileDialog1.FileName);
Once we have got the configuration
object, we can directly access appSetting
and connectionString
values by using the relevant properties it provides. The LoadSingleSection()
method loads a single section from connection string sections collection by using the name of that section. ConnectionStringsSettingsCollection
implements IDictionary
so it becomes easy to access node attributes using famous operator []
. The following code demonstrates that:
NameValueCollection appSettingCollection = configuration.AppSettings.Settings;
string windowState = appSettingCollection["WindowState"];
ConnectionStringSection connectionStringSection = configuration.ConnectionStrings;
ConnectionStringsSettingsCollection sectionCollection =
connectionStringSection.LoadSingleSection("connection1");
string connectinString = sectionCollection["connectionString"]);
string connectionName = sectionCollection["name"]);
string dbProvider = sectionCollection["provider"]);
We can also read custom sections from our configuration.xml using GetSection()
method of configuration
object. Another important method of this manager class is GetAllChildsOfSection()
which returns all child nodes of a section. The purpose to include this method is that it gives us <add>
nodes and their key value pairs of a specific section. It works just like AppSettings
property of configuration
which returns key value of <AppSetting>
section. The following code snippet shows how to use both GetSection()
and GetAllChildsOfSection()
methods.
ConfigurationSection customSection = configuration.GetSection("globalThreshold");
string amount = customSection["amount"];
NameValueCollection generalConfigurationsCollection =
configuration.GetAllChildsOfSection("//cofigSection/general");
Though we have used OpenFileDialog
to location the file, in real scenario where component/class library would be used by web/windows form application,
we can put configuration.xml in the same directory from where the component will be loaded.
Configuration configuration = manager.OpenExeConfiguration
(System.AppDomain.CurrentDomain.BaseDirectory+@"Configuration.xml");
History
- 8th March, 2007: Initial post