Adding and accessing custom sections in your C# App.config
Update (Feb 10, 2016): I found a NuGet package called simple-config that allows you to dynamically bind a section in your web/app.config file to a strongly typed class without having to write all of the boiler-plate code that I show here. This may be an easier solution for you than going through the code I show below in this post.
So I recently thought I’d try using the app.config file to specify some data for my application (such as URLs) rather than hard-coding it into my app, which would require a recompile and redeploy of my app if one of our URLs changed. By using the app.config it allows a user to just open up the .config file that sits beside their .exe file and edit the URLs right there and then re-run the app; no recompiling, no redeployment necessary.
I spent a good few hours fighting with the app.config and looking at examples on Google before I was able to get things to work properly. Most of the examples I found showed you how to pull a value from the app.config if you knew the specific key of the element you wanted to retrieve, but it took me a while to find a way to simply loop through all elements in a section, so I thought I would share my solutions here.
Due to the popularity of this post, I have created a sample solution that shows the full implementation of both of the methods mentioned below.
Download the source code example
Simple and Easy
The easiest way to use the app.config is to use the built-in types, such as NameValueSectionHandler
. For example, if we just wanted to add a list of database server urls to use in my app, we could do this in the app.config file like so:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="ConnectionManagerDatabaseServers" type="System.Configuration.NameValueSectionHandler" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<ConnectionManagerDatabaseServers>
<add key="localhost" value="localhost" />
<add key="Dev" value="Dev.MyDomain.local" />
<add key="Test" value="Test.MyDomain.local" />
<add key="Live" value="Prod.MyDomain.com" />
</ConnectionManagerDatabaseServers>
</configuration>
And then you can access these values in code like so:
string devUrl = string.Empty;
var connectionManagerDatabaseServers = ConfigurationManager.GetSection("ConnectionManagerDatabaseServers") as NameValueCollection;
if (connectionManagerDatabaseServers != null)
{
devUrl = connectionManagerDatabaseServers["Dev"].ToString();
}
Sometimes though you don’t know what the keys are going to be and you just want to grab all of the values in that ConnectionManagerDatabaseServers
section. In that case you can get them all like this:
// Grab the Environments listed in the App.config and add them to our list.
var connectionManagerDatabaseServers = ConfigurationManager.GetSection("ConnectionManagerDatabaseServers") as NameValueCollection;
if (connectionManagerDatabaseServers != null)
{
foreach (var serverKey in connectionManagerDatabaseServers.AllKeys)
{
string serverValue = connectionManagerDatabaseServers.GetValues(serverKey).FirstOrDefault();
AddDatabaseServer(serverValue);
}
}
And here we just assume that the AddDatabaseServer()
function adds the given string to some list of strings.
One thing to note is that in the app.config file, <configSections>
must be the first thing to appear in the <configuration>
section, otherwise an error will be thrown at runtime. Also, the ConfigurationManager class is in the System.Configuration namespace, so be sure you have
using System.Configuration
at the top of your C# files, as well as the System.Configuration
assembly included in your project’s references.
So this works great, but what about when we want to bring in more values than just a single string (or technically you could use this to bring in 2 strings, where the “key” could be the other string you want to store; for example, we could have stored the value of the Key as the user-friendly name of the url).
More Advanced (and more complicated)
So if you want to bring in more information than a string or two per object in the section, then you can no longer simply use the built-in System.Configuration.NameValueSectionHandler
type provided for us. Instead you have to build your own types. Here let’s assume that we again want to configure a set of addresses (i.e. urls), but we want to specify some extra info with them, such as the user-friendly name, if they require SSL or not, and a list of security groups that are allowed to save changes made to these endpoints.
So let’s start by looking at the app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="ConnectionManagerDataSection" type="ConnectionManagerUpdater.Data.Configuration.ConnectionManagerDataSection, ConnectionManagerUpdater" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<ConnectionManagerDataSection>
<ConnectionManagerEndpoints>
<add name="Development" address="Dev.MyDomain.local" useSSL="false" />
<add name="Test" address="Test.MyDomain.local" useSSL="true" />
<add name="Live" address="Prod.MyDomain.com" useSSL="true" securityGroupsAllowedToSaveChanges="ConnectionManagerUsers" />
</ConnectionManagerEndpoints>
</ConnectionManagerDataSection>
</configuration>
The first thing to notice here is that my section is now using the type ConnectionManagerUpdater.Data.Configuration.ConnectionManagerDataSection
(the fully qualified path to my new class I created) and ConnectionManagerUpdater
(the name of the assembly my new class is in). Next, you will also notice an extra layer down in the <ConnectionManagerDataSection>
which is the <ConnectionManagerEndpoints>
element. This is a new collection class that I created to hold each of the Endpoint entries that are defined. Let’s look at that code now:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConnectionManagerUpdater.Data.Configuration
{
public class ConnectionManagerDataSection : ConfigurationSection
{
/// <summary>
/// The name of this section in the app.config.
/// </summary>
public const string SectionName = "ConnectionManagerDataSection";
private const string EndpointCollectionName = "ConnectionManagerEndpoints";
[ConfigurationProperty(EndpointCollectionName)]
[ConfigurationCollection(typeof(ConnectionManagerEndpointsCollection), AddItemName = "add")]
public ConnectionManagerEndpointsCollection ConnectionManagerEndpoints { get { return (ConnectionManagerEndpointsCollection)base[EndpointCollectionName]; } }
}
public class ConnectionManagerEndpointsCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new ConnectionManagerEndpointElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((ConnectionManagerEndpointElement)element).Name;
}
}
public class ConnectionManagerEndpointElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("address", IsRequired = true)]
public string Address
{
get { return (string)this["address"]; }
set { this["address"] = value; }
}
[ConfigurationProperty("useSSL", IsRequired = false, DefaultValue = false)]
public bool UseSSL
{
get { return (bool)this["useSSL"]; }
set { this["useSSL"] = value; }
}
[ConfigurationProperty("securityGroupsAllowedToSaveChanges", IsRequired = false)]
public string SecurityGroupsAllowedToSaveChanges
{
get { return (string)this["securityGroupsAllowedToSaveChanges"]; }
set { this["securityGroupsAllowedToSaveChanges"] = value; }
}
}
}
So here the first class we declare is the one that appears in the <configSections>
element of the app.config. It is ConnectionManagerDataSection
and it inherits from the necessary System.Configuration.ConfigurationSection class
. This class just has one property (other than the expected section name), that basically just says I have a Collection property, which is actually a ConnectionManagerEndpointsCollection
, which is the next class defined.
The ConnectionManagerEndpointsCollection
class inherits from ConfigurationElementCollection
and overrides the required fields. The first tells it what type of Element to create when adding a new one (in our case a ConnectionManagerEndpointElement
), and a function specifying what property on our ConnectionManagerEndpointElement
class is the unique key, which I’ve specified to be the Name field.
The last class defined is the actual meat of our elements. It inherits from ConfigurationElement
and specifies the properties of the element (which can then be set in the xml of the App.config). The ConfigurationProperty
attribute on each of the properties tells what we expect the name of the property to correspond to in each element in the app.config, as well as some additional information such as if that property is required and what it’s default value should be.
Finally, the code to actually access these values would look like this:
// Grab the Environments listed in the App.config and add them to our list.
var connectionManagerDataSection = ConfigurationManager.GetSection(ConnectionManagerDataSection.SectionName) as ConnectionManagerDataSection;
if (connectionManagerDataSection != null)
{
foreach (ConnectionManagerEndpointElement endpointElement in connectionManagerDataSection.ConnectionManagerEndpoints)
{
var endpoint = new ConnectionManagerEndpoint() { Name = endpointElement.Name, ServerInfo = new ConnectionManagerServerInfo() { Address = endpointElement.Address, UseSSL = endpointElement.UseSSL, SecurityGroupsAllowedToSaveChanges = endpointElement.SecurityGroupsAllowedToSaveChanges.Split(',').Where(e => !string.IsNullOrWhiteSpace(e)).ToList() } };
AddEndpoint(endpoint);
}
}
This looks very similar to what we had before in the “simple” example. The main points of interest are that we cast the section as ConnectionManagerDataSection
(which is the class we defined for our section) and then iterate over the endpoints collection using the ConnectionManagerEndpoints
property we created in the ConnectionManagerDataSection
class.
Also, some other helpful resources around using app.config that I found (and for parts that I didn’t really explain in this article) are:
How do you use sections in C# 4.0 app.config? (Stack Overflow) - Shows how to use Section Groups as well, which is something that I did not cover here, but might be of interest to you.
How to: Create Custom Configuration Sections Using Configuration Section (MSDN)
ConfigurationSection Class (MSDN)
ConfigurationCollectionAttribute Class (MSDN)
ConfigurationElementCollection Class (MSDN)
I hope you find this helpful. Feel free to leave a comment. Happy Coding!
Comments
Juan
The first one works fine for me (in this case in particular), thanks for the article!
Michael
I believe; this row: [ConfigurationCollection(typeof(ConnectionManagerEndpointsCollection), AddItemName = “add”)]
Should be placex over the Coolection class and NOT the Section class, besides from that, I thank you for your fine article.
deadlydog
@Michael How I have it written (with the attribute in the Section class) seems to work fine for me.
Brent
Great, only issue is Section references System.dll not System.Configuration so should be,
Luke
Thanks for this, clearest example I found
Richard
Great article, thanks. Just wonder if you can show the code for the AddEndPoint method called in the last example?
deadlydog
@Richard Hey Richard, I’ve just updated this post to include a full source code example showing how to fully implement the methods mentioned in this post. Take a look and you should find all of the details that you need.
Paul Sodimu
Very useful and clear. Thanks
janna
Awesome post! thanks!
Simone
Fantastic, very useful :) thanks very much
Ori
Finally! fast and simple. Thanks!
Charlie
Just perfect, THANKS for sharing this with os !!!!!!!!!!
Ankit
Thank you so much. thanks for this article. I really needed this code in this article. Thank you * 1000
Arjen
very well explained. One of the few really working tutorials out there
Vamsi
Hi , When I am debugging the attached source code, I am getting this error {“The type initializer for ‘AppConfigExample.AdvancedExamplesData.Endpoints’ threw an exception.”}.
Same happening for Environments and DataCenters.
Can you help me in solving this pls?
I am using VS 2010 and Target Frame Work: .NET Framework4 Client Profile
deadlydog
@Vamsi I just downloaded the source code nad opened it up in VS 2010 and everything worked fine for me. Are there any more details in the exception?
Vamsi
In Program.cs till Console.WriteLine(“\nEndpoints:”); its working fine.
First run of foreach is failing. foreach (var endpoint in AdvancedExamplesData.Endpoints.EndpointsList) { .. } It is failing in this part ,’AdvancedExamplesData.Endpoints.EndpointsList’ of foreach loop with error “The type initializer for ‘AppConfigExample.AdvancedExamplesData.Endpoints’ threw an exception.”
Vamsi
in EndPoint.cs file.. i replaced
//var customSection = ConfigurationManager.GetSection(CustomConfigurationSection.SectionName) as CustomConfigurationSection;
to below and that works.. var test = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); var customSection = test.GetSection(CustomConfigurationSection.SectionName) as CustomConfigurationSection;
Looks like this is a bug in .NET Framework 4.
And sorry to mention lately.. Thank a lot for a quick reposne and wonderful explantion and code.
luisa
thank you for the article
Sirish
This is a great read, very useful. Thank you!
Gopinath
You are none other than my well wisher. Thanks a lot for such a excellent.smart method of custom config sections.
Jerry
Thanks Dan. This was very helpful.
Samarth
Thanks a lot Dan.. just got it what exactly needed. :)
Samuel Tremblay
By far, the best article about this topic that I found.
Zak
Firstly, this is a great article so please don’t take offence at what I am about to write. My problem is that configuration in .Net is an afterthought, neglected and a disaster. This example is great in that you could use it to define a custom and meaningful pointer to better configuration mechanisms.
All that configuration should be is pointers to the locations of configuration. This is synonymous with having a database connection string that points to data (which is itself, a form of configuration).
A huge amount of code is needed just to produce something as simple as create a configuration section in a .net config file. I admire this article for it’s effort but would you be happy creating multiple classes just to set up some configuration sections? For me, the best strategy for managing configuration is to use something like ConfigR. you can declare objects inside text files that are direct mirrors of the objects you intend to use.
There is poorly formed attribute based xml in this example, another bane of .Net configuration, so it is hard to understand the structure of the intended configuration - let alone the intended use of the configuration so whilst we may know that something is a connection string, connection string to what? Quickly, there ends up being redundant configuration that serves no purpose.
The final nail in the coffin of .Net configuration is that once you have gone through the process of implementing configurationElement and configurationSections, carefully hard coding the keys to the configuration values, inside your code, somewhere you have to map the configuration items to arguments supplied to various classes etc.
I am completing an MVC project and I will publish an alternative on something like CodeProject on how configuration should be done but please try and think about how configuration should be implemented.
Rex
I really appreciate the clear, concise, thorough example and explanation. Very helpful, thanks!
Pawel
Tutorial was very helpful. Thanks.
Michael
First of all, thank you so much for this post. It has been very useful.
I do have one question. In your sample application, again thank you so much for providing a working sample application, when you add the training environment, does that get persisted to the app.config file? If so, where does that live? (I’ve looked at the AppConfigExample.exe.config in the bin directory, but it doesn’t appear to have been updated.) If it doesn’t get persisted back, how would you implement something along those lines.
Thanks again for a great article!
deadlydog
@Michael Thanks Michael. The Training environment is not persisted back to the AppConfigExample.exe.config file; it is just retained in memory. So the next time you run your app it will not have the Training environment. If you want it persisted I guess you would need to write the added values to a file somewhere, and then on load have your app check that file for any additional values to add to the list. You could perhaps try writing directly to the AppConfigExample.exe.config file, but I’m not sure if that’s the best idea as if there’s a bug you could end up mangling the .config file and not be able to launch the app anymore.
Seif
Excellent! Thank you much
Creating and using a custom configuration section in web/app config – coding times
[…] helped me understand a few basics but I’ve found a more concessive tut Adding and accessing custom sections in your C# App.config that explained a lot more and had more complex […]
Rick O'Shea
Which begs the question… why does it take pages of boilerplate to read in a key-value list? This really does highlight just how heavy (and needlessly so) .NET can be. Who would guess it would take more than 10 lines of code (because in NodeJS for example, it would be).
zak willis
Hello, thanks for this post. I am in Switzerland on holiday with only my mobile phone. I am going to be writing a book which will involve lambasting before resolving one of the worst elements and biggest jokes in static code development - configuration. I have used simpleconfig for a simple purpose, to be able to avoid having to replicate keys across different application and test project. If you rely solely upon simple config a multiproject solution will end up with huge config files. It is barely acceptable that 3rd party libraries put massive sections inside config files but to have references to configuration manager but to try and mirror that just to try and be like these guys is insane. My solution is to use simpleconfig to get a few keys but to use configr to return configured classes instead. If we think about an application it only exists because of state that entails data and data can be thought of as being dynamic or static. Configuration is just a slower moving set of pointers to faster moving data. With all this information, the least any application deserves is to get data in the format it expects and not spending countless time referring to keys and sections throughout the application. My solution is to store dictionary / and to inject iconfigurator with a method that allows for a dictionary lookup to return that poco. The advantage of this approach is that we could swap the concrete disk based config poco to a completely different source altogether by changing the bindings. In my world, the only thing config files contain are pointers to .net config files for use by simpleconfig and text files to point to poco implementation. I will try and put some code up at some point but it isn’t rocket science, just creating a few classes to expose data to applications in the format they recognise rather than jumping through hoops to add custom config sections or mapping keys.
sam
Great tutorial. Really helps me. Have nice days!
Achintya
Great Article. Very useful
sven7
so powerful, great!
kyle
your “more complicated” solution worked perfectly!
Dave Wilkins
Still very helpful and clear in the year 2020. Thanks!
Leave a Comment
Your email address will not be published. Required fields are marked *