Custom .NET XML .config sections with SparkyTools.XmlConfig
Tags: csharp, dotnet
Published: 2021-07-07 14:51:54

> *This article is about dying technology - .NET framework* **web.config** and **app.config** *XML files.*

> *.NET Core overhauled application configuration, jettisoning .config XML files in favor of app settings JSON files, and .NET 5 brings those configuration changes back into "Framework" .NET.*

> *For those of us who are still acting as caretakers for geriatric .NET framework apps using .config files though, here's a tool to make it a bit easier...*

If you want to load configuration data for .NET application from a **web.config** or **app.config** section other than the built-in sections (e.g. appSettings, connectionStrings), you have to write a custom [IConfigurationSectionHandler](https://msdn.microsoft.com/en-us/library/system.configuration.iconfigurationsectionhandler(v=vs.110).aspx) implementation to handle your custom .config section. It’s not hard to do, but it’s a fairly tedious coding exercise. What if you didn’t have to?...

## SparkyTools.XmlConfig NuGet packages
- **SparkyTools.XmlConfig**: for .NET framework v4.6 and above
  - [NuGet package](https://www.nuget.org/packages/SparkyTools.XmlConfig/)
  - [Documentation](https://github.com/BrianSchroer/SparkyTools/blob/master/SparkyTools.XmlConfig/api.md)
- **SparkyTools.XmlConfig.Fx**: for .NET framework v4-4.5.1
  - [NuGet package](https://www.nuget.org/packages/SparkyTools.XmlConfig.Fx/)
  - [Documentation](https://github.com/BrianSchroer/SparkyTools/blob/master/SparkyTools.XmlConfig.Fx/api.md)

> The code in these packages was inspired by a blog post called "[The Last Configuration Section Handler I'll Ever Need](https://sites.google.com/site/craigandera/craigs-stuff/clr-workings/the-last-configuration-section-handler-i-ll-ever-need)" by Craig Andera that I read way back in 2003 🤯. I've been using this technique since then, and finally "NuGet-ed" it a few years ago.

### ConfigurationSectionDeserializer / ConfigurationSectionListDeserializer

These SparkyTools.XmlConfig classes makes it easy to load a strongly-typed object (or IList of objects) from a custom **web.config** or **app.config** file section without having to write a custom IConfigurationSectionHandler implementation.

For the code examples below, I'll be using this incredibly realistic C# class definition:

```csharp
public class Foo
{
    public string Bar { get; set; }
    public decimal Baz { get; set; }
}
```

In the .config file, register each custom section with a type of “SparkyTools.XmlConfig.**ConfigurationSectionDeserializer**” or
“SparkyTools.XmlConfig.**ConfigurationSectionListDeserializer**”:

```xml
<configuration>
  <configSections>
    <section name="Foo" type="SparkyTools.XmlConfig.ConfigurationSectionDeserializer, SparkyTools.XmlConfig" />
    <section name="FooList" type="SparkyTools.XmlConfig.ConfigurationSectionListDeserializer, SparkyTools.XmlConfig" />
  </configSections>
```
(If you're using the SparkyTools.XmlConfig.*Fx* package, the types/namespaces will be "...XmlConfig.*Fx*.Config")

In each registered custom section, specify the object type via the type attribute. Here's an single instance section:

```xml
<Foo type="FooNamespace.Foo, FooAssemblyName">
    <Bar>bar</Bar>
    <Baz>123.45</Baz>
</Foo>
```
...and a “list” section: (Note that "type" is the "single" instance type, not "IList..."):

```xml
<FooList type="FooNamespace.Foo, FooAssemblyName">
    <Foo>
        <Bar>bar1</Bar>
        <Baz>111.11</Baz>
    </Foo>
    <Foo>
        <Bar>bar2</Bar>
        <Baz>222.22</Baz>
    </Foo>
</FooList>
```

You can use **XmlAttribute** attributes in your class definitions to tell the serializers to get properties from XML attributes rather than child XML elements:

```csharp
public class Foo
{
    [XmlAttribute("bar")]
    public string Bar { get; set; }

    [XmlAttribute("baz")]
    public decimal Baz { get; set; }
}
```
```xml
<FooList type="FooNamespace.Bar, FooAssemblyName">
    <Foo bar="bar1" baz="111.11" />
    <Foo bar="bar2" baz="222.22" />
</FooList>
```

To read from your custom .config section, just call the ConfigurationSectionDeserializer or ConfigurationSectionListDeserializer **Load** method, specifying the object type and the .config section name:

```csharp
Foo foo = 
    ConfigurationSectionDeserializer.Load<Foo>("Foo");

IList<Foo> fooList = 
    ConfigurationSectionListDeserializer.Load<Foo>("FooList");
```
***
### Dependency Injection

My [SparkyTools.DependencyProvider](https://www.nuget.org/packages/SparkyTools.DependencyProvider) NuGet package can be used to create **DependencyProvider**<*T*> instances for constructing classes with dependencies that aren't easily mockable. DependencyProvider<*T*> just has one method, "GetValue()", which returns a *T* instance.

Here's a class with a dependency of type "Foo" injected via a DependencyProvider:

```csharp
using SparkyTools.DependencyProvider;

public class Qux
{
    private readonly Foo _foo; 

    public Qux(IDependencyProvider<Foo> fooProvider)
    {
        _foo = fooProvider.GetValue();
    }
}
``` 

The static **ConfigurationSectionDeserializer.DependencyProvider** and **ConfigurationSectionListDeserializer.DependencyProvider**
methods create DependencyProviders for loading data from custom .config file sections.

Here's code for creating an instance of the Qux class shown above, injecting the Foo dependency from a custom .config file section:

```csharp
using SparkyTools.XmlConfig;
. . .
var qux = new Qux(
  ConfigurationSectionDeserializer.DependencyProvider<Foo>("Foo"));
```
####AppSettings.DependencyProvider
A bit off this post's topic of custom .config file sections, but still in the general XML configuration topic - The static **AppSettings.DependencyProvider()** method can be used to make classes that get values via **ConfigurationManager.AppSettings** more unit-testable... 

Consider this class:

```csharp
public class Quux
{
    public void DoThing()
    {
        if (ConfigurationManager.AppSettings["featureEnabled"] == "true")
        {
            // Do that thang!
        }
    }
}
```

If you want to unit test it, you'll have to either have an app.config file with an appSettings section in your test project or have your test code do a static method call to set the value, e.g.: 
```csharp
ConfigurationManager.AppSettings["serviceUrl"] = "http://fakeurl";
``` 
...and doing static things in unit tests is always a bad idea.

Instead of using ConfigurationManager.AppSettings directly you can abstract your class to say "I need a function that returns configuration values for given keys":

```csharp
public class Quux
{
    private readonly Func<string, string> _getAppSetting;

    public Quux(IDependencyProvider<Func<string, string> appSettingsProvider)
    {
        _getAppSetting = appSettingsProvider.GetValue();
    }

    public void DoThing()
    {
        if (_getAppSetting("featureEnabled") == "true")
        {
            // Do that thang!
        }
    }
}
```
SparkyTools.XmlConfig.**AppSettings.DependencyProvider()** creates a DependencyProvider<*Func*<*string, string*>>) that "wraps" **ConfigurationManager.AppSettings**, so you can use it in your "real code":

```csharp
using SparkyTools.XmlConfig;
...
    var qux = new Quux(AppSettings.DependencyProvider());
```
...and "mock" the appSettingProvider dependency in your unit tests.