ConfigurationProcessor.AspNetCore 1.12.0

dotnet add package ConfigurationProcessor.AspNetCore --version 1.12.0
NuGet\Install-Package ConfigurationProcessor.AspNetCore -Version 1.12.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ConfigurationProcessor.AspNetCore" Version="1.12.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ConfigurationProcessor.AspNetCore --version 1.12.0
#r "nuget: ConfigurationProcessor.AspNetCore, 1.12.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install ConfigurationProcessor.AspNetCore as a Cake Addin
#addin nuget:?package=ConfigurationProcessor.AspNetCore&version=1.12.0

// Install ConfigurationProcessor.AspNetCore as a Cake Tool
#tool nuget:?package=ConfigurationProcessor.AspNetCore&version=1.12.0

ConfigurationProcessor.DependencyInjection

NuGet NuGet

NuGet NuGet

This library registers and configures services in a service collection using .NET's' configuration library.

Example

Given an application with the following ConfigureServices section:

services.AddLogging();
services.AddHsts(options =>
{
   options.ExcludedHosts.Clear();
   options.Preload = true;
   options.IncludeSubDomains = true;
   options.MaxAge = TimeSpan.FromDays(365);
});
services.Configure<CookiePolicyOptions>(options =>
{
   options.HttpOnly = HttpOnlyPolicy.Always;
   options.Secure = CookieSecurePolicy.Always;
});

The ConfigureServices method above can be moved into the configuration using the following code and appsettings.config configuration:

// .NET 5.0, .NET Core 3.1 and below using ConfigurationProcessor.DependencyInjection
services.AddFromConfiguration(Configuration, "Services");

// .NET 6.0 with ConfigurationProcessor.AspNetCore
var builder = WebApplication.CreateBuilder(args);
builder.AddFromConfiguration("Services");
{
   "Services": {
      "Logging": true,
      "Hsts": {
         "ExcludedHosts": {
            "Clear": true
         },
         "Preload": true,
         "IncludeSubDomains": true,
         "MaxAge": "356.00:00:00"
      },
      "Configure<Microsoft.AspNetCore.Builder.CookiePolicyOptions>": {
         "HttpOnly": "Always",
         "Secure": "Always"
      }
   }
}

Since we are using IConfiguration, we aren't limited to the appsettings.json for configuring our services. We can also have the configuration in environment variables, in command-line arguments or with custom configuration providers such as AWS Secrets Manager.

Basics

The library works by using reflection and scanning all currently loaded assemblies for extension methods for IServiceCollection. This project was inspired by the Serilog.Settings.Configuration project.

Extension method mapping and overload resolution

Given a configuration named MyService, an extension method named AddMyService or MyService will be filtered from the candidate extension methods. If multiple candidates are found, the best overload will be chosen based on the name of the input parameters.

Given the following extension methods:

public IServiceCollection AddMyService(this IServiceCollection services);
public IServiceCollection AddMyService(this IServiceCollection services, string name);
public IServiceCollection AddMyService(this IServiceCollection services, int count);

When given the configuration below, the extension method AddMyService(IServiceCollection, int) is chosen.

{
   "Services": {
      "MyService" : {
         "Count": 23
      }
   }
}

If the extension method is parameterless, use true instead of an object. The configuration method below will choose AddMyService(IServiceCollection). A false value will prevent registration.

{
   "Services": {
      "MyService" : true
   }
}

Action Delegate mapping

ConfigurationProcessor can be used with extension methods that use a generic action delegate. Generic arguments type of System.Object is not supported.

Given the extension method below:

public IServiceCollection AddMyService(this IServiceCollection services, Action<MyServiceOptions> configureOptions);

public class MyServiceOptions
{
   public string Title { get; set; }
}

The configuration below is equivalent to calling services.AddMyService(options => {});:

{
   "MyService" : true
}

The configuration below is equivalent to calling services.AddMyService(options => { options.Title = "Mr" });:

{
   "MyService" : {
      "Title": "Mr"
   }
}
Action delegates with more than one argument

When the action delegates with more than one argument, all matching configuration methods will be called.

Given the extension method below:

public IServiceCollection AddMyService(this IServiceCollection services, Action<MyServiceOptions, MyOtherServiceOptions> configureOptions);

public class MyServiceOptions
{
   public string Title { get; set; }
   public string Name
}

public class MyOtherServiceOptions
{
   public string Title { get; set; }
}

The configuration below is equivalent to calling services.AddMyService((options, other) => { options.Title = "Mr"; options.Name = "John"; other.Title = "Mr"; });:

{
   "MyService" : {
      "Title": "Mr",
      "Name": "John"
   }
}

Generic extension method mapping

Generic extension methods can be mapped by supplying the generic parameter via the angle brackets <>. The full name of the type must be supplied.

public IServiceCollection AddMyService<T>(this IServiceCollection services, T value);
{
   "MyService<System.String>" : {
      "Value": "hello world"
   }
}

Mapping to extension methods with a single array parameter

Extension methods that have a single array parameter can be mapped with json arrays. This will work with or without the params keyword. This can also work with a List<> parameter type.

public IServiceCollection AddMyService(this IServiceCollection services, params string[] values);
{
   "MyService" : [
      "salut",
      "hi",
      "konnichiwa"
   ]
}

Mapping to extension methods with a single dictionary parameter

Extension methods that have a single dictionary parameter can be mapped with json objects. Currently, only the parameter type Dictionary<,> is supported. If one of the property names matches with a parameter name in an extension method overload, the overload with the matching parameter name is chosen instead of the dictionary overload.

public IServiceCollection AddMyService(this IServiceCollection services, Dictionary<string, int> values);
{
   "MyService" : {
      "Value1": 1,
      "Value2": 2
   }
}

Supported string mappings

Some .NET types can be mapped from a string in configuration.

.NET Type Example Configuration C# Equivalent
System.Type "System.String" Type.GetType("System.String")*
System.Reflection.Assembly "MyCorp.MyLib" Assembly.Load("MyCorp.MyLib")*

*For Type and Assembly, the loaded types and assemblies are searched first before resorting to Type.GetType or Assembly.Load.

Delegate mapping and static members

A parameter/property type that is a delegate can be mapped to a static method.

namespace MyProject
{
   public static class Helpers
   {
      public IServiceCollection AddMyService(
         this IServiceCollection services,
         Action<MyConfiguration> configureAction);

      public static void LogOnError(ILogger instance, MyConfiguration configuration);
   }

   public class MyConfiguration
   {
      public Action<ILogger, MyConfiguration> OnError { get; set; }
   }
}

The configuration below maps to services.AddMyService(config => config.OnError = Helpers.LogOnError)

{
   "MyService" : {
      "OnError": "MyProject.Helpers::LogOnError"
   }
}

Execution Order

ConfigurationProcessor uses IConfiguration.GetChildren() to retrieve the methods to execute. The configuration key names are sorted alphabetically, and there is no supported mechanism to retrieve the original sort order.

Given the configuration below:

{
   "MyService" : {
      "ConfigureB": { "Value": 1 },
      "ConfigureZ": { "Value": 5 },
      "ConfigureA": { "Value": 4 }
   }
}

The order of the methods executed will be ConfigureA, ConfigureB and then ConfigureZ;

ConnectionString handling

Parameters or properties that are named 'ConnectionString' are given special handling where the value will come from an element from ConnectionStrings section with the same key if it exists.

Given the configuration below:

{
   "ConnectionStrings": {
      "Default": "abcd"
   },
   "Services": {
      "DbConnection": {
         "ConnectionString" : "Default"
      }
   }
}
public static IServiceCollection AddDbConnection(this IServiceCollection services, string connectionString)

The configuration above is equivalent to calling services.AddDbConnection("abcd");

Credits

Serilog.Settings.Configuration which is the basis for this project.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.12.0 137 9/3/2023
1.11.0 187 3/25/2023
1.10.1 328 11/14/2022
1.9.0 339 11/12/2022
1.8.2 327 11/12/2022
1.7.2 353 11/4/2022
1.7.1 359 10/25/2022
1.6.1 368 10/24/2022
1.6.0 392 10/23/2022
1.5.1 367 10/22/2022
1.5.0 358 10/22/2022
1.4.0 409 10/11/2022
1.3.0 369 10/7/2022
1.2.1 378 10/6/2022
1.2.0 397 9/19/2022
1.1.1 396 9/16/2022
1.1.0 385 9/2/2022
1.0.0 434 7/7/2022
0.9.9 424 7/5/2022
0.9.8 415 7/5/2022
0.9.7 407 7/4/2022
0.9.5 409 7/3/2022
0.9.4 426 7/3/2022
0.9.3 439 7/3/2022
0.9.2 411 7/3/2022
0.9.1 410 7/2/2022

v1.12.0
  - Specify license as Apache 2.0
v1.11.0
  - Added support for mapping to Dictionary<T, Action<TArg>>
v1.10.0
  - Support Action delegates of any number of parameters as configuration targets.
v1.9.0
  - Support Action delegates of up to 7 generic arguments as configuration targets.
v1.8.1
  - Support Action<T1, T2> and Action<T1, T2, T3> as configuration targets.
v1.7.2
  - Added special handling for retrieving ConnectionStrings
  - Lambda parameters can now be in any position
v1.6.1
  - Fixed dynamic invocation issues with optional parameters
v1.6.0
  - Added IConfigurationProcessor interface to dynamically call methods
v1.5.1
  - Fixed exception with optional parameters
v1.5.0
  - Improve selection of single parameter overload method.
  - Improve binding to readonly properties
v1.4.0
  - Improve discovery of executable methods to include implemented interfaces
v1.3.0
  - Add support for disambiguating overloads with different array types.
  - Prefer overloads with the most matching parameters
v1.2.1
  - Fix MissingMethodException when there exists a candidate method with no parameters is not chosen correctly.
v1.2.0
  - Added interception for missing extension method event
v1.1.1
  - Ignore TypeLoadException when scanning for extension methods
v1.1.0
  - Added support for binding with interface configuration actions
v1.0.0
  - Initial release