Configuration 2 – Part 3

Last time I covered a JSON extension to my IConfigurationFactory type. I demonstrated how my extension makes it very easy to read a local JSON file and return the results as an IConfiguration object. I also said that I would continue the theme for one more article by covering a method I wrote to allow the configuration itself to determine where my configuration settings come from. I’ll do that now.

Let’s start by quickly summarizing what I’ve already done.

So, the ConfigurationBuilder type, from the Microsoft.Extensions.Configuration library, does a great job of creating IConfiguration objects at runtime that contain configuration settings from any number of different sources. Setting up a builder to create those objects requires some repetitive code that we’ve managed to minimize by introducing our IConfigurationFactory type, and our associated extension methods. Now, as a result, creating an IConfiguration object with data from, say, an app.config file, or a JSON file, only requires a single line of code.

We could improve the situation a little further though if we made an extension method for the IConfigurationFactory type that would read from the local configuration and then use that single setting to determine where we should read the bulk of the remaining configuration settings from. That way, instead of directly calling FromAppConfig, or FromAppSettings, in our code, we could simply call a FromDefault method, and have our local configuration decide what “default” means. In other words, I envision something like this:

 

This allows us to vary the source of our configuration settings without ever having to change a single line of code. We only have to change a single setting in the local app.config. That way, if we need a different configuration source for testing, reproducing a bug, conducting a demo, or for any reason, really, it only requires us to change a single configuration setting.

That’s what I’m after now.

So, to start, let’s look at the code for my new extension method and then I’ll walk through my implementation:

 

The first thing we do is read from the local configuration. We do that so we can ask the configuration where our “default” configuration source is. Once we have that IConfiguration object, we bind it to an instance of ConfigurationSettings, which is a class that we use to hold our configuration related settings (we’ll look at the implementation of ConfigurationSettings a bit later).

Once we’ve read the configuration, and bound the results to our ConfigurationSettings object, we then do a quick check to see if “default”, in this case, is actually the app.config file. IF it is, then we can return the local configuration and we’re done.

Most of the time, “default” won’t mean “app.config” though, so the next thing we do is look in the current AppDomain for a list of previously loaded assemblies. After we get that list we then filter out a few common assemblies that we know won’t ever contain one of our extension methods. Next we loop through the remaining assemblies and check each one for types that support one or more of our extension methods. When we find an extension method that matches our signature and naming convention (starts with “From”, is an extension method, and has at least one parameter that is an IConfigurationFactory object), we then call it and return the resulting IConfiguration object.

The ConfigurationSettings class that I talked about earlier can be seen here:

 

So, just a model class that creates a default value of “AppConfig” for the “Default” property. In order to set the value to anything else, you’d just need to add a key/value to your local configuration for:

“AppName:Configuration:Default”

Where “AppName” is the name of your application.

The code that binds the object to a section in the configuration, “BindSettings”, is also mine. Here is the source code for that:

 

This method, hung off the IConfiguration type, allows for quickly binding a settings object to a specific configuration section. The method makes use of a naming convention I like to use where I place application specific configuration settings under a section named for the application I’m trying to configure. That makes more sense when you consider that I typically use a shared configuration service for most of my application level configurations.

The method starts by creating a GlobalSettings object, which it then binds to the IConfiguration object that was passed as a parameter. Once that’s done, we use the SafeFriendlyName property to ensure that we have a reasonable rendition of the current AppDomain’s name. Next, we create an empty instance of whatever settings type was passed as the type parameter. Then we go looking for a configuration section to bind to. Using my naming convention, the method tries to bind to a combination of the friendly name we got earlier and the sub-section name passed in as a parameter. If that section doesn’t exist, we simply return. If it does, we bind our settings object to it and then we return.

So, as I wrote this article I noticed a couple of things about this, my first crack at a “FromDefault” method: (1), the reflection code in my FromDefault method is already part of my CG.Reflection library, and (2), the way I’ve written this method now doesn’t leave room for storing the AppName:Configuration:Default key/value pair in the appsetttings.json file. It forces everyone to use the app.config file instead.

The first issue may require me to remove some duplicate code that I should have pulled in from my other NUGET package. No biggie there. The second issue is trickier, since fixing it will require some careful thinking about packaging. You see, I typically package my NUGET code in such a way that nobody pays a penalty for anything until they use it. Take my CG.Configuration.Json package, for instance, I added all the JSON related code there so that anyone who simply wanted to use CG.Configuration wouldn’t have to pay a penalty for any 3rd party JSON dependencies until (or unless) they decided to use my CG.Configuration.Json package. So, for this method, if I also want to be able to read the “AppName:Configuration:Default” key/value pair from the appsettings, then I’ll have to decide how to package it in such a way that nobody pays a penalty for that if they don’t care anything about JSON files.

Those kinds of conundrums are exactly the kind of thing I like figuring out, so, I’m sure I’ll come up with something. Until then, I hope everyone enjoyed the articles. I hope someone benefits from my ideas and my code.

 

See ya next time! :o)

 

The code for this article is taken from my CG.Configuration.Default NUGET package, which can both be downloaded for free at: https://github.com/CodeGator/CG.Configuration.Default

The source code for the CG.Configuration.Default project live on Github and can be obtained for free at: https://www.nuget.org/packages/CG.Configuration.Default

 

 

Photo by Senne Hoekman from Pexels