Configuration Management in Legacy .NET Applications (.NET Framework 4.5.1+)
Most of the web-based .NET applications are using appSettings section of the web.config or/and a static class (usually called ApplicationConfiguration or similar) to store configurations. In rare cases, you can find that someone is using custom web.config sections and map them into the code, which is honestly not the most pleasant thing to do. Through the time amount of configuration keys is growing and web.config files are blowing up to unreadable sizes. Maintainability of the configuration becomes a hard job which nobody want to do.
As soon as you are doing CI/CD and building for different environments (staging/production, etc.) you also start to get the pain of changing configs during the build time. Sure, you can use web.config transformations approach or similar solutions. But more environments you will have — more transforms will appear in the project folder.
What about security? We often store sensitive information in our code and config files. Just try to search that kind of information in code and web.configs of any legacy application, I bet you will find half a dozen of production tokens, hardcoded logins, passwords, and secret URLs.
In .NET world people were struggling and having problems with configuration for a long time. Web.config and hardcoded values — definitely not the way how configuration management should be done!
Web.config is obsolete. Is there a better solution?
.NET Core Team and Community have been working hard on a new platform with fresh ideas and great implementations! Let’s take a look at the brand new API for Configuration in ASP.NET Core. ConfigurationBuilder class and extension methods provide an easy way for setting up the sources and behaviours of the configuration on application start:
As we can see, there is support for loading JSON files, environment variables, and others out of the box. There is also a number of other useful features, like:
- Reloading configuration without application restart
- Configuration snapshots (versions)
- Easy way of adding custom data providers
- Safe way of working with encrypted data and data stores like Azure Key Vault
With new API there is no need to store the whole configuration in one big static class. With new Options Framework you can map different parts of the configuration tree to separate objects:
Or even complex object graphs:
Usage is really simple: just inject IOptions<T>(where T is your Option class) into class by using favourite IoC container:
That is really awesome, but we are not running .NET Core!
.NET Core is a shiny future
All know that the migration to the new platform is nearly impossible for legacy systems, but is there a possibility to use some parts of it? Thanks to the new approach that .NET Core team is doing — now each part is a separate Nuget Package. So if your system is running .NET Framework 4.5.1+ you can easily install those packages and use them from your legacy code, because of the compatibility layer, called .NET Standard. I would not go into details about how and why it works. If you want to know more about .NET Standard, here is an awesome video “.NET Standard — Introduction” by Immo Landwerth, which cover all key aspects.
Unfortunately, if you are using .NET Framework lower than 4.5.1 you will not be able to use .NET Standard libraries (most of the libraries, for example, Options Framework, targets 4.5.1+).
Using ASP.NET Core Configuration API and Options in .NET
The new Configuration API works perfectly on .NET 4.5.1+ but there is a tiny problem with Options Framework — it was designed to be used together with .NET Core. As result, all the extension methods of Options Framework are using the new Dependency Injection Abstraction, which is not present in old .NET Framework. There are two possible ways of consuming Options Framework from old code:
- Install and use .NET Core Dependency Injection Abstraction.
- Reimplement extension methods, using our current IoC Container API.
The first option is doable but would cause a lot of issues, collisions, and conflicts between existing and new DI mechanisms.
So let’s take a look at the second way as it is easier to use in the legacy system.
First of all, we need to reimplement registration of Options Framework with using our DI (in examples we will use Autofac):
Then we will need two methods that allow us to register Option mapping classes. First is the way of registration:
And second — takes delegate as an argument:
Now we are able to register our Option mappings in the beginning of the DI setup:
Each injection of IOptions<MailingOptions> or IOptions<FeedOptions> will be resolved as an instance of a proxy class that contains mapped data.
One of the great features of Options framework is that we have hot reloading of the configuration without restarting the application pool (as you remember, each time when you change web.config, the application pool is restated). To use reloadable configuration we need to add reloadOnChange parameter to AddJsonFile method and inject IOptionsSnapshot<T> instead of IOptions<T>. Now on any change of JSON file, Options Framework will inject the latest version of the configuration. Also, you can mix injections of IOptionsSnapshot<T> and IOptions<T> to allow only partial hot reloading.
There are many other features in the new .NET platform. You can learn more about them here.
.NET Standard allows us to consume many of new features and improve the developer experience in different aspects such as configuration management, dependency injection, cross platform capabilities and many others. We are able to start using all those new things in legacy projects right now, without any migration steps!
Don’t miss the possibility to use brand new features in old applications, that can help you to learn and use fresh technologies even in legacy systems.
Here is a link to the demo project, that showcase usage of Options Framework in .NET 4.5.1 and newer.