So yesterday, I was creating a custom configuration section in web.config for an ASP.NET application. I did not had direct contact with the administrators who would administer the day-to-day operations of the application, and needed to make them aware of the various options available for the custom configuration section together with their quick description. I decided to display the options and their basic description in the Administration UI of the application itself.

As you might be aware, each configuration section in web.config or app.config (for web or desktop applications respectively) must have a type also defined that inherits from ConfigurationSection class.

Further, the class must have properties marked with ConfigurationProperty attribute each such public property corresponding to an attribute or element for your custom configuration section.

I needed 2 things: 1) Have a Xml serialized string of my custom section with current values from web.config, and any missing values filled with the defaults defined for them, so that the admins can see all possible configuration options together with their current values; and 2) A basic description for each value involved.

The first of them turned out to be more troublesome than I thought. I tried various ways to serialize the ConfigurationSection derived class, but all failed.

Trying to serialize with System.Xml.Serialization.XmlSerializer gave a “You must implement a default accessor on System.Configuration.ConfigurationLockCollection because it inherits from ICollection” InvalidOperationException. I was unable to overcome this exception even with XmlAttributeOverrides defined for all offending properties.

Then I tried more ways, e.g. GetRawXml() method of SectionInformation (got a “This operation does not apply at runtime” InvalidOperationException), and overriding SerializeSection for the ConfigurationSection. But overriding almost required manual serialization of all known options for the section without an automatic way of doing it.

And after many more tries with different techniques (e.g. this link, this link, this link, this link and this link), when nothing worked (the last one works for a desktop app, but not for a web app), and I almost decided to hard-code the serialization process manually with known options, I gave a last try to the following, which actually worked:

 

{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }public string serialize ()
{
return (this.SerializeSection(null, “myCustomSection”, ConfigurationSaveMode.Full));
}{/syntaxhighlighter}

Here I simply added the above method to my ConfigurationSection class, and voila, calling it returned the perfect xml representation of the section I wanted, even with default values for missing options in web.config. It would have been pretty hard to guess it was so simple.

I would again repeat, the advantage of the above is that the section’s defined options in web.config are used, but missing options are filled with their default values as specified.

For the second part of my requirements (generating a basic documentation of all options), I simply decorated all the ConfigurationProperty properties with a System.ComponentModel.DescriptionAttribute, and then a basic recursive call extracted the documentation required. Here’s the interesting portion of the code for the same (extracting information from DescriptionAttributes):

 

{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }public string description
{
get
{
if (this._description == null)
{
Type configPropType=typeof(ConfigurationPropertyAttribute);
this._description = this.getDescription(this.GetType().GetProperties(), “”, 0);
}
return (this._description);
}
}

private string getDescription (PropertyInfo[] properties, string indent, int level)
{
StringBuilder builder=new StringBuilder();

int i=1;
foreach (var p in properties)
{
var descAttrs=p.GetCustomAttributes(typeof(DescriptionAttribute), true);
if (descAttrs.Length > 0)
{
builder.Append(indent);
if (level > 0)
{
builder.Append(level).Append(‘.’);
}
builder.Append(i).Append(“. “);
builder.Append(p.Name).Append(” – “);
builder.Append(((DescriptionAttribute) descAttrs[0]).Description);
builder.Append(‘\n’);
builder.Append(this.getDescription(p.PropertyType.GetProperties(), indent + ” “, level + 1));
i += 1;
}
}

return (builder.ToString());
}{/syntaxhighlighter}

So, I used the string returned by the serialize() call as the xml required in web.config for the custom config section, and the string returned by description as its documentation in the UI.

The sample code is attached below. It would work for the following custom config section:

 

 

<myCustomSection>
	<webFarm enabled="true" syncMode="WebService">
		<webServiceSync webFarmAppUrls="http://localhost/myapp;http://server2/myapp" />
	</webFarm>
</myCustomSection>