Rants and Raves

Thanks for visiting my blog!

Building a .NET Core Configuration Source
Building a .NET Core Configuration Source
April 15, 2018

As I’ve been teaching ASP.NET Core for a while now, some things I’ve been saying I’ve taken on faith. One of these was that building a Configuration Source (a provider that can read configuration and feed it into the configuration system) is fairly easy.

With a break in building my Vue course, I was curious so I decided to build a configuration source that I hope no one uses. It is a configuration source that reads the AppSettings in a web.config file. This is a thought exercise, not a recommendation.

Building the Configuration Source

In order to be a Configuration Source, you must derive from the IConfigurationSource interface. You could implement this interface directly, or if your configuration is file based (like the one I built), you can simply derive from FileConfigurationSource. This class deals with most of the problem of opening and reading a file. Since my Configuration Source is to read the web.config, that’s perfect.

In my constructor, I just set some options that are really about how FileConfigurationSource handles a file:

  public class AppSettingsConfigurationSource : FileConfigurationSource
  {
    public AppSettingsConfigurationSource()
    {
      Path = "web.config";
      ReloadOnChange = true;
      Optional = true;
      FileProvider = null;
    }

    public override IConfigurationProvider Build(IConfigurationBuilder builder)
    {
      EnsureDefaults(builder);
      return new AppSettingsConfigurationProvider(this);
    }
  }

The only thing I really need to do with the Source itself is to override the Build method and just return a new provider that does the actual work.

Building the Provider

The provider is where all the real work happens. Like the Configuration Source, there is an interface you can implement: IConfigurationProvider. For my needs, I am just going to use the FileConfigurationProvider as the base class as it goes well with the FileConfigurationSource:

  public class AppSettingsConfigurationProvider : FileConfigurationProvider
  {
    public AppSettingsConfigurationProvider(AppSettingsConfigurationSource source) 
      : base(source)
    {

    }

```csharp

The work is done, in the case of **FileConfigurationProvider**, by overriding the Load method:


```csharp
    public override void Load(Stream stream)
    {
      try
      {
        Data = ReadAppSettings(stream);
      }
      catch
      {
        throw new FormatException("Failed to read from web.config");
      }
    }

It passes a stream in that contains the contents of the file specified in the Configuration Source. At this point I just need to read the file and set the Data to a Dictionary<string,string>.

Finally, I implement this new method (ReadAppSettings) just by using old, boring System.Xml code:

    private IDictionary<string, string> ReadAppSettings(Stream stream)
    {
      var data = 
        new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);

      var doc = new XmlDocument();
      doc.Load(stream);

      var appSettings = doc.SelectNodes("/configuration/appSettings/add");

      foreach (XmlNode child in appSettings)
      {
        data[child.Attributes["key"].Value] = child.Attributes["value"].Value;
      }

      return data;
    }

With the implementation complete, we need to have a way to register it when we setup the configuration.

Adding Sources to Configuration

If you’re just reading this to figure out how to use my provider, you can get it from Nuget, though I’d prefer you migrated your AppSettings to another configuration format. But if I can’t talk you out of it, just add it to the project via the dotnet command-line:

> dotnet add package WilderMinds.Configuration.AppSettings

I could have required that you just add the source via the Add method:

public static IWebHost BuildWebHost(string[] args) =>
  WebHost.CreateDefaultBuilder(args)
         .ConfigureAppConfiguration(cfg =>
         {
           cfg.Sources.Clear();
           cfg.Add<AppSettingsConfigurationSource>(s => s.Path = "web.config");
         })
         .UseStartup<Startup>()
         .Build();

This is awkward, so I think I’d prefer to create a convenient method to just call “AddAppSettings()” to make it easier. To do this, I just write an extension method for the IConfigurationBuilder so we can add it easily:

  public static class AppSettingConfigurationExtensions
  {
    public static IConfigurationBuilder AddAppSettings(this IConfigurationBuilder bldr)
    {
      return bldr.Add(new AppSettingsConfigurationSource());
    }
  }

This allows the code to do the setup for configuration to be simply:

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration(cfg =>
            {
              cfg.Sources.Clear();
              cfg.AddAppSettings();
            })
            .UseStartup<Startup>()
            .Build();

If you want to look at the source code, you can get it on Github:

https://github.com/shawnwildermuth/ConfigurationAppSettings

Let me know if you have a better way to do any of this!