Configuration 2 – Part 1

For configuring my .NET Core applications I, like so many other .NET developers, turn to the Microsoft.Extensions.Configuration library for help. The library is well designed and it greatly simplifies the process of reading configuration settings from a variety of sources and formats.

The library exposes a type named IConfiguration to return the actual configuration settings. It exposes a type named ConfigurationBuilder to create those IConfiguration instances at runtime. It also exposes a variety of extension method on the ConfigurationBuilder type that make it super easy to read configuration settings from a number of common sources, like json files, xml files, command line arguments, etc.

Overall, the library is a joy to work with. There is one area though where I thought I might be able to improve it. We’ll see, at the end of the article, if I’ve actually managed to do so. :o)

So, here’s a pretty typical scenario for creating an IConfiguration instance with the Microsoft.Extensions.Configuration library:

 

Obviously, I’ve taken some liberties in the example above, but, we can all see that creating an IConfiguration instance usually involves: (1) creating a builder object and then (2) calling one or more extension methods on that builder object in order to setup one or more configuration sources for the builder, and then (3) calling any number of methods to configure those sources before, finally, (4) calling the build method to create the actual IConfiguration object.

I’m not saying there’s anything wrong this approach, just that I sometimes get tired of writing these steps over and over again. What would be nice, would be if the Microsoft.Extensions.Configuration library had included a factory type that would have included simple methods for creating IConfiguration objects from the sources that I use most often – like the app.config, or the appsettings.json, or even my shared, REST based configuration service. It would be nice if those methods made reasonable assumptions so that I wouldn’t have to include too many parameter values to get everything to work. That way, I could quickly read configuration values from the places I usually get those sorts of things from, and I would still have the ability to manually create a builder and go through the steps if I needed to read configuration settings from some other location.

Yeah, I wish Microsoft had included a factory for those scenarios. I think that would have improved the usability of an already very good library. They didn’t, however, so, I did it for them.

So, how would the example above look if I had my factory to work with: Let’s look at that:

 

Some might argue that this is not enough of a difference to warrant creating a factory type and all the additions to that factory that would be required to read from various common configuration sources. I respect that argument, but I also disagree. I think wrapping up the most common configuration scenarios would reduce code duplication and allow for testing that factory code in a QA project so that everyone knows that it works before they even try to use it. On the other hand, if you disagree then feel free to keep writing those steps over and over again.

So, to start off, I thought about what I might need for a factory, and how I might use a factory in my projects, and I started off with a single type that I named IConfigurationFactory . Here is the code for that type:

 

Yes, it’s supposed to be empty. Let me explain. By creating an empty type, then deriving from that type with a concrete class, then extending everything through extensions method hung off the type, I can do several things at once. I can:

  1. I can start with a simple type that doesn’t do anything more than it should do at the moment. And even though I expect this type to grow, over time (that’s the whole point, actually), I’m not trying to see into the future and add types to the interface now that I hope everyone will use down the road.
  2. I can create my concrete implementation of an IConfigurationFactory object without forcing anyone else to use that type in their own projects. Even if you decide to create your own concrete factory type, it’s ok, the extension methods we’ll use later, to do actual work, will be hung off the IConfigurationFactory type so they won’t care that you’ve rolled your own factory type – as long as you implement IConfigurationFactory.
  3. I can add features to the type later on, as I identify the need, without cluttering up the interface today with methods that might not ever get used. That approach, trying to foresee future needs today, never works. It only tends to trigger landslide changes in larger projects, creates code bloat, and generally tends to make everything harder to test.

So with all that in mind, here is my concrete implementation of IConfigurationFactory:

 

Yes, it still supposed to be empty. We’ll add extension methods next to add features as we identify the need. In taking this approach, we’ll never need to crack this class open again and that’s what makes it possible for you, or anyone else, to create their own concrete class type, later, if the need arises.

So, what is this SingletonBase class? It’s part of the CG.Core library, which is available on GITHUB and NUGET, and is what I use to create singleton types in my libraries. Yes, I know, singletons are a handy handy design patter but they sometimes cause their own issues. Hey, that’s why I’m making it so you can create your own concrete factory class later, if you want to. You’re welcome. :o)

Alright, so, I introduced the IConfigurationFactory type. I introduced the ConfigurationFactory singleton type. Now let’s look at an extension method to actually created an IConfiguration object at runtime …

The most obvious place to look for configuration settings has to be the app.config file. Yes, I know, not every .NET application will have an app.config file. .NET Core applications, for instance, seem to be greatly deprecating the use of app.config. Still, there are tons of plain vanilla .NET apps and libraries out there that still use app.config and probably will continue to do so for the forseeable future. So, let’s tackle reading configuration settings from the app.config file first.

But wait! You say there’s already a ConfigurationManager class that does that? Yes, that’s true, and it does a pretty good job of it, as well, but, it also requires an additional NUGET package in some situations, and, it doesn’t return settings as an IConfiguration object. So, we’ll need to create an extension off the IConfigurationFactory type, to handle reading settings from an app.config file. Let’s look at that now.

Here is the simplified code I wrote to read app-settings from an app.config file and return them wrapped in an IConfiguration object:

 

There are two methods on this class. Let’s start with the first, and then move on to the second.

The public method is named FromAppConfig, and use used with the IConnectionFactory type to read from the app.config file. If we look at the method we’ll notice a few things that I intend to codify into an informal convention, of sorts, for these extensions methods:

  1. The method starts with the word “From”. Later, as we write additional extension methods to handle additional configuration sources, each new method will also start with the word “From”. That way, they’ll all line up nicely in IntelliSense.
  2. The method accepts no parameters. I may come to regret this one later, but, for right now, I think each method should be targeted enough so that it shouldn’t require any parameters in order to just work.
  3. The method returns an IConfiguration object. That’s the point of all this work, so, I think it’s reasonable to insist that any extension methods hung off the IConfigurationFactory type should return a valid IConfiguration object.

The very first thing the method does is create an instance of GlobalSettings. This is a type that I’ve created to hold configuration settings that aren’t tied to any one application, or library, or whatever. Later, when we get into using binding with IConfiguration objects, we’ll see how the values in a setting class, like GlobalSettings, can be “overridden” at runtime, by binding the settings object to an IConfiguration object. I’ll present the source for GlobalSettings later in the article, but, for now, just know that it contains information we’ll need, later, to find the app.config file.

Once we have the GlobalSettings object, we then create a ConfigurationBuilder object. We’ll use that builder in a bit to create the configuration source. Next, we call the ReadAppConfig method, to return a list of key-value-pair objects, read from the app.config file. Once we have those key-value-pair objects, we use LINQ to order them and remove any duplicates, then we inject them into an in-memory configuration source using the call to AddInMemoryCollection. That method is part of the Microsoft library and it allows us to build an IConfiguration obcjt using nothing more than an in-memory collection of key-value-pair objects – perfect for this situation.

Once we’ve added the in-memory source to the ConfigurationBuilder, the only thin left to do is call the Build method and return the results.

There’s a little more going on in the next method. This one is private, since it’s really just an implementation detail, and it is named ReadAppConfig. The method accepts the GlobalSettings object that created earlier, as a parameter. The first thing we do in this method is to build a file patch to an app.config file. Of course, we can’t just go look for a file called “app.config” because the .NET convention is that the app.config file is named after the application or library that uses it. So, for instance, if you have an application called Mine.exe, then the app.config for that program would be a file named Mine.exe.config. Because of that, we need to know the name of the current application in order to find the app.config. That’s where the GlobalSetting object comes in. It contains a property named FriendlyName, that we’ll use to determine how to build a path to the app.config file for the current application. Well, AppDomain really…

Some of you might know that there’s already a FriendlyName property on the AppDomain class, so, why duplicate that? Well, it turns out that the FriendlyName property on the AppDomain class sometimes, in certain environments, doesn’t contain what we might think it should. For instance, in a unit testing environment is sometimes contains a bit of text along with an embedded path to the fixture assembly. That’s one of the reasons for the GlobalSettings class, so we can control what the friendly name of our application is, even if we’re running in a QA fixture.

So, having built a path to the app.config file, we then check to see if the file exists. If not, we simply return. On the other hand, if the file does exists then we read it in using the Load method on the XmlDocument object. That gives us way to quickly parse through the XML using the methods on the XmlDocument object. Next, we look for an appSettings node in the XML document using the SelectSingleNode method. Is we don’t find the node we simply return. If we do, we then loop through any child nodes and process each one using a switch statement. If the node is an “add” it means we should add a new key-value-pair to the collection. If the node is a “clear” it means we should remove anything from the list of key-value-pair objects. If the node is a remove it means we should remove a specific key-value-pair from the collection. The node types “add”, “remove” and “clear” are part of standard .NET so it’s pretty safe to assume that these are the types we’ll encounter. Once we’ve build the list of key-value-pair objects, representing the contents of the appSettings node in the app.config file, we return it.

You might wonder why I wrote code to manually parse the app.config file. After all, I could have used the ConfigurationManager class, already a part of .NET, to read that information for me. Well, again, while that would have worked it also would have required an additional NUGET package for most of the environments I work with, so, I didn’t use that route.

You might also wonder why I didn’t use the XML source that’s a part of the Microsoft.Extensions.Configuration library to read the appSettings portion of the app.config file. I actually tried that and it turns out, that XML source won’t read XML with namespaces, which means it won’t read a standard app.config file, which uses namespaces. So, yeah, the Microsoft XML configuration source won’t read a Microsoft XML configuration file. I thought that was funny …

So, using everything I’ve presented so far, this is now how we can quickly read a configuration from our local app.config file:

 

In my mind, that’s not bad. After all, it’s hard to beat a single line solution. Yes, we could simply have used ConfigurationManager.AppSettings to do the same thing, but then we’d have still needed to return the results as an IConfiguration. This is an easier method. And, it’s one that we can expand, later, to deal with reading configuration sources from other places.

In the meantime, I promised that I would show a listing for the GlobalSettings class. Here’s that code now:

 

Let’s go through this quickly. The class contains two properties: FriendlyName and SafeFriendlyName. We’ve talked about FriendlyName before, this is the property that mirrors the AppDomain.FriendlyName property, except that it also works in a test environment. SafeFriendlyName is simply the FriendlyName property with any trailing file extension stripped out. That’s because AppDomain.FriendlyName also, sometimes, includes a trailing .exe, or .dll in the string that it returns. Some of my code doesn’t want or need that trailing file extension so I created SafeFriendlyName so I wouldn’t have to deal with that.

The GlobalSettings constructor calls the DefaultFriendlyName method to create a default for the FriendlyName property. This value might be changed, later, if the caller decides to bind this object to an IConfiguration, and, if that IConfiguration happens to contain another value for GlobalSettings:FriendlyName. Which brings me to the other reason for putting the FriendlyName property here (rather than reading it directly from AppDomain.FriendlyName), you can override the value in your configuration by using the same Binding mechanism that is already part of the Microsoft.Extensions.Configuration library. Here’s an example:

 

So, yeah, add the right setting in your configuration, then bind it to a GlobalSettings object, and you can effectively rename your application, on the fly. Pretty cool stuff.

So, that’s it for this article. Next time I’ll cover another factory extension method, this time for reading configuration settings from an appsettings.json file. See ya then!

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

 

The source code for the CG.Configuration project live on Github and can be obtained for free at:

https://www.nuget.org/packages/CG.Configuration

 

 

 

Photo by Startup Stock Photos from Pexels