Decoupling My Configurations

I’ve spent a lot of time over the last couple months re-thinking the configuration settings in my .net applications.  I love .net core’s configuration setup.  It is a breeze to set up, and it allows you to easily change application behavior without changing source.  I came across this concept while hunting down how to work Kestrel’s HTTPS settings after upgrading to .Net Core 2.0.  Like a bunch of developers, I discovered that I could no longer just pass in an HTTPS address as an application argument.  Luckily I was able to find a brilliant solution provided by the user raffaeler.  Raffler’s  solution, that i’ll dig into another time, was outstanding, and it is also what introduced me to the concept of Configuration Objects.

What are configuration Objects

The JSON layout

At it’s most basic, Core’s configuration implementation is easy to understand, but also extremely cumbersome.  The appsettings.json objects are pretty striaght forward, but unless you dig into microsoft’s documentation, you are going to miss out on some cool implementation strategies like configuration objects.

The appsettings.json objects are your basic json objects, I will focus on Kestrel server settings.  Inside the appsettings.json files, i had the following configurations…

{
  "Kestrel_Server": {
    "Endpoints": {
      "Http-001": {
        "IsEnabled": false,
        "Address": "0.0.0.0",
        "Port": "0000"
      },
      "Https-001": {
        "IsEnabled": true,
        "Address": "0.0.0.0",
        "Port": "6054",
        "Certificate": {
          "Source": "File",
          "Path": "certificate.pfx",
          "Password": "fakePassword"
        }
      }
    }
  }
}

As you can see, I had described a kestrel server.  The server could have multiple endpoints.  Each endpoint has a value defining if it is enabled, the ip4 address, and port.  In addition the endpoints I want to be secured, I included information for a certificate.  Getting these details was causing a lot of effort.

  • Server
    • Endpoints
      • Http-001
        • isEnabled
        • Address
        • Port
      • Https-001
        • isEnabled
        • Address
        • Port

 

I was using the Configuration[“Value”] method to extract exact values from the loaded configuration.  For instance to get Https-001 ‘s port, I would have to use
var port = Configuration["Kestrel_Server:EndpointsHttps-001:Port"];
However, this was very difficult to keep straight in my head. But what raffaeler’s solution showed me, was how to build a class to hold the configuration data as fields. Something much easier to understand and use in a program.

The Configuration Classes

First, the new classes.

public class Host
    {
        public Dictionary<string, EndPoint> Endpoints {get; set;}
    }

    public class EndPoint
    {
        public bool IsEnabled {get; set;}
        public string Address {get; set;}
        public int Port {get; set;}
        public Certificate Certificate {get; set;}
    }

    public class Certificate
    {
        public string Source {get; set;}
        public string Path {get; set;}
        public string Password {get; set;}

    }

The certificate class will hold the three certificate parameters for any key value labeled “Certificate”. The EndPoint class will get all of the endpoint properties, if they exist. Note that there is no storage for the endpoint name, that is because it gets loaded into the Host class. The host class only has a dictionary field. When loaded, the endpoint names are loaded into the key value of the dictionary, the rest of the values are instantiated into endpoint objects (with certificates if they were defined) and loaded into the value portion of the dictionary.

And what magic gets this data translation to happen? A single line of code.

var host = configuration.GetSection("Kestrel_Server").Get<Host>();

The Get method will map corresponding labels to the correct structures based on name.  The one that threw me for a while was the Host’s dictionary field for endpoints, but that is because I do not use dictionary structures that often.  As I said above, the endpoint labels are made into the keys, and the endpoint structures are loaded into the values.  Very nifty.

If you want to see how I used this in my code, checkout my source code for Casperinc.IdentityProvider.API.  I used this configuration extraction technique to generate configuration objects for both the Kestrel server, and for the OpenIdDict configuration.  The configuration classes will be in a single source file /Helpers/ConfigurationClasses.cs.  The source files startup.cs and program.cs will have the instances of the configuration file being mapped to the configuration objects.


Leave a Reply