Skip to content

Repository files navigation

KeyInject

📦 KeyInject is a .NET library designed to inject values into configurations using regular expression patterns, with support for nested patterns.
⚙️ This facilitates dynamic and flexible configuration management in .NET applications.
🔄 Configuration hot-reloads supported.

📋 Table of contents

🧩 Features

  • Regex-Based Injection: Utilize regular expressions to identify and replace placeholders in your configuration files.
  • Nested Pattern Support: Handle complex configurations with nested placeholders seamlessly.
  • ReplaceNullValue: When enabled (default), missing or empty placeholder values are replaced with an empty string; when disabled, placeholders are left unchanged.
  • Easy Integration: Designed for straightforward integration into existing .NET projects.
  • Supported versions: net5.0 through net10.0

📜 Installation

Install KeyInject using NuGet Package Manager:

Install-Package KeyInject

Or via the .NET CLI:

dotnet add package KeyInject

All versions can be found here.

🚀 Quick Start

Look at actual examples here ./examples;

Basic example

In appsettings.json:

{
"ConnectionStrings": {
"Main": "server=${Database:Server};user=${DB_USER};password=${DB_PASSWORD}"
}
}

In Environment variables:

DB_USER=addyDB_PASSWORD=greenlover

In user secrets:

{
"Database": {
"Server": "1.4.8.8"
}
}

In Program.cs file:

usingKeyInject;varbuilder=WebApplication.CreateBuilder(args);// Configuration order is up to you.// Remember, that ConfigurationProviders overrides each other!builder.Configuration.AddJsonFile("appsettings.json");builder.Configuration.AddEnvironmentVariables();// ✅ Add Key Injection exactly at the latest position builder.Configuration.AddKeyInject();// add services ...varapp=builder.Build();varconn=app.Configuration.GetConnectionString("Main");awaitConsole.Out.WriteLineAsync(conn);// ✅ Output: server=1.4.8.8;user=addy;password=greenloverawaitapp.RunAsync();

⚙️ Basic Configuration

From appsettings.json

KeyInject always enriches from appsettings.json.
It's not neccessary to provide json configuration.
By default, ${_} pattern will be used if no other patterns provided.
(All the patterns will be described below)

Example configuration

{
"KeyInject": {
// simply enable or disable globally "Enabled": true,
// allows to reload configuration if other Configuration Provides triggers OnReload()"SetReloadEnabled": true,
// ignore case of pattern key group >> ${IgNore_Case_Of_thIs_woRD}
"IgnoreCase": true,
// set how many time config will be injected to resolve circular dependencies"ReplaceRepeatCount": 10,
// replace null/missing values; "name=${NAME}" --> "name=""ReplaceNullValue": true,
// from preset patterns ${_}, <<_>> ...// if Patterns is empty, "${_}" pattern will be used anyway"Patterns": [
"${_}", "{{_}}", "$<_>", "<<_>>", "!{_}!", "%_%"
],
// adding custom regex pattern. // warn! must use ?<key> regex group, see documentation.// no exception throw on bad regex."RegexPatterns": [
"!\\{(?<key>[^{}]+)\\}!"
],
// adding custom prefixes"KeyPrefixes": [
"PRE_", "DATABASE_"
]
}}

Extended configuration see in 💉 Dependency Injection part.

🔑 Key Prefix

Prefixes used to specify that we only want to replace keys starts with some value.
In example, for configured prefix DB_:
🚫 DATABASE_USER_PASSWORD - wont be replaced
DB_USER_PASSWORD - woill be replaced

💡Notice!
If you specified prefixes, then only those patterns that start with this prefix will be replaced.

Can be registered with:

WebApplication.CreateBuilder(args).Configuration.AddKeyInject(b =>b.AddKeyPrefix("PRE_").AddKeyPrefix("DATABASE_"));

Or with appsettings:

{
"KeyInject": {
"KeyPrefixes": [
"PRE_", "DATABASE_"
]
}
}

🎭 Patterns

Preset patterns

💡 Here is six default pattern names:
${_}{{_}}$<_>
<<_>>!{_}!%_%

Can be registered with:

WebApplication.CreateBuilder(args).Configuration.AddKeyInject(b =>b.AddPresetPattern("${_}").AddPresetPattern("$<_>"));

Or with appsettings:

{
"KeyInject": {
"Patterns": [
"${_}", "{{_}}", "$<_>", "<<_>>", "!{_}!", "%_%"
]
}
}

⚠️ If no Pattern presented in explicit configuration will be used default: ${_}

Their detailed description:

  1. ${_}
  • regex: \$\{(?<key>[^\{\}]+)\}
  • example: ${SOMEKEY}, ${some_key_2}
  1. {{_}}
  • regex: \{\{(?<key>[^\{\}]+)\}\}
  • example: {{SOMEKEY}}, {{some_key_2}}
  1. $<_>
  • regex: \$<(?<key>[^<>]+)>
  • example: $<SOMEKEY>, $<some_key_2>
  1. <<_>>
  • regex: <<(?<key>[^<>]+)>>
  • example: <<SOMEKEY>>, <<some_key_2>>
  1. !{_}!
  • regex: !\{(?<key>[^{}]+)\}!
  • example: !{SOMEKEY}!, !{some_key_2}!
  1. %_%
  • regex: %(?<key>[^%]+)%
  • example: %SOMEKEY%, %some_key_2%

⚠️ Notice!
You must specify preset pattern name exactly in provided format!
Pattern names like "${...}" instead of ${_}is not supported!

Of course, you can use multiple patterns at the same time.

🔧 Custom patterns

You can use custom Regex patterns with builder or appsettings configuration.
You must to specify ?<key> regex group in pattern, like:

!\{(?<key>[^{}]+)\}!

⚠️ Group naming must be exactly: key

Custom pattern registration examples:

  • appsettings.json :
{
"KeyInject": {
"RegexPatterns": [
"!\\{(?<key>[^{}]+)\\}!"
]
}
}
  • with DI builder:
builder.Configuration.AddKeyInject(b =>b// adding custom regex pattern. Warn! Must to use ?<key> regex group, see documentation.// no exception throw on bad regex..AddRegexPattern(@"!\{(?<key>[^{}]+)\}!")// notice, adding built Regex CAN throw exception if regex text was incorrect.AddRegexPattern(newRegex(@"!\{(?<key>[^{}]+)\}!")));

🪆 Nested patterns

You can use nested patterns, by default supports 5 levels of nesting.
Here is an example of nesting:

  1. In appsettings.json
{
"Connection": "${CONN}"
}
  1. In Environment variable:
CONN="server=${DB_IP};user=${DB_USER};password=${DB_PASSWORD}"
  1. In Vault config provider (or some else provider too):
DB_IP=1.2.3.4DB_USER=rootuserDB_PASSWORD=password123
  1. Result configuration string will be:
voidDisplayConfig(IConfigurationconfig){Console.WriteLine(config["Connection"]);// ✅ Output: server=1.2.3.4;user=rootuser;password=password123}

⚠️ Default supported nesting for 5 levels, and it's enough for most cases.

You can change levels count with:

Configuration.AddKeyInject(b =>b.SetReplaceRepeatCount(10));

or in appsettings.json:

{
"KeyInject": {
"ReplaceRepeatCount": 10
}
}

💉 Dependency Injection

⚠️ Warning!
Use .AddKeyInject() after other Configuration Provides!

Example:

varbuilder=WebApplication.CreateBuilder(args);// ... builder.Configuration.AddKeyInject(b =>b// simply enable or disable globally .SetEnabled(true)// allows to reload configuration if other Configuration Provides triggers OnReload().SetReloadEnabled(true)// keep placeholder when value is missing or empty.SetReplaceNullValue(false)// adding custom prefixes.AddKeyPrefix("PRE_").AddKeyPrefix("DATABASE_")// adding custom regex pattern. Warn! Must to use ?<key> regex group, see documentation.// no exception throw on bad regex..AddRegexPattern(@"!\{(?<key>[^{}]+)\}!")// notice, adding built Regex CAN throw exception if regex text was incorrect.AddRegexPattern(newRegex(@"!\{(?<key>[^{}]+)\}!"))// from preset patterns ${_}, <<_>> ....AddPresetPattern("${_}")// set how many time config will be injected to resolve circular dependencies.SetReplaceRepeatCount(10)// ignore case of pattern key group >> ${IgNore_Case_Of_thIs_woRD}.SetIgnoreCase(true)// choose yor custom config section instead default "KeyInject", first way:.EnrichFromAppSettings(builder.Configuration.GetSection("MyCustomSection"))// second way:.EnrichFromAppSettings(c =>c.GetSection("MyCustomSection")));

🪵 Logging

You can add logging to log configuration sources and replaced keys.
Simply pass ILoggerFactory, example:

varbuilder=WebApplication.CreateBuilder(args);// ... other config sources ...builder.Configuration.AddKeyInject(LoggerFactory.Create(b =>b.SetMinimumLevel(LogLevel.Debug).AddConsole().AddDebug()));

Console output example:

info: KeyInject.Injection.InjectionProcessor[0]
KeyInject starting process configuration
info: KeyInject.Injection.InjectionProcessor[0]
KeyInject configuration: {"Enabled":true,"ReplaceRepeatCount":5,"IgnoreCase":true,"KeyPrefixes":[],"RegexPatterns":["\\$\\{(?\u003Ckey\u003E[^\\{\\}]\u002B)\\}"]}
info: KeyInject.Injection.InjectionProcessor[0]
Found configuraion providers:
MemoryConfigurationProvider
EnvironmentVariablesConfigurationProvider Prefix: 'ASPNETCORE_'
EnvironmentVariablesConfigurationProvider Prefix: 'DOTNET_'
JsonConfigurationProvider for 'appsettings.json' (Optional)
JsonConfigurationProvider for 'appsettings.Development.json' (Optional)
EnvironmentVariablesConfigurationProvider
Microsoft.Extensions.Configuration.ChainedConfigurationProvider
JsonConfigurationProvider for 'appsettings.json' (Required)
info: KeyInject.Injection.InjectionProcessor[0]
For key [ConnectionStrings:Main] made [3] replacements info: KeyInject.Injection.InjectionProcessor[0]
For key [ConnectionStrings:Some:Data:Key-3] made [1] replacements info: KeyInject.Injection.InjectionProcessor[0]
Configuration loaded

💡 The logging configuration is optional.
You don't have to pass ILoggerFactory to the AddKeyInject(), just call without parameters if logging is not needed.

☕ Contribution

If you wanna to buy me a coffee 😃, I will be grateful for any tokens in TON network:
💎 noncommunicado.ton
💎 UQD0zFgp0p-eFnbL4cPA6DYqoeWzGbCA81KuU6BKwdFmf8jv

About

Inject values in configuration by regex patterns with nested patterns support

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages