Adds support for placeholders in any configuration source (e.g. appsettings.json).
Install the ConfigurationPlaceholders package from NuGet:
Package manager:
Install-Package ConfigurationPlaceholders
Or via the .NET CLI
dotnet add package ConfigurationPlaceholders
You can add ConfigurationPlaceholders to your project with the AddConfigurationPlaceholders
extension method.
var builder = WebApplication.CreateBuilder( args );
builder
.AddConfigurationPlaceholders( new InMemoryPlaceholderResolver( new Dictionary<String, String?>
{
{ "FQDN", fullDomainName }
} ) );
This will replace the placeholder ${FQDN}
with the fully qualified name of the machine running the application.
Placeholder in the appsettings.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"CertificateSubject": "${FQDN}"
}
You can specify any number of placeholder resolvers IPlaceholderResolver
in the call to AddConfigurationPlaceholders
.
Later added placeholder resolvers IPlaceholderResolver
will override values from before added placeholder resolvers IPlaceholderResolver
.
By default ConfigurationPlaceholders will check if values are provided for all placeholders. If there are any missing placeholder values a ConfigurationPlaceholderMissingException
will be thrown.
You can change this behavior by passing another MissingPlaceholderValueStrategy
to AddConfigurationPlaceholders
.
The following strategies are available:
Will check if values are provided for all placeholders. If there are any missing placeholder values a ConfigurationPlaceholderMissingException
will be thrown.
You can think of this one as a lazy execution version of VerifyAllAtStartup
.
A ConfigurationPlaceholderMissingException
will be thrown when a configuration entry with a missing placeholder value is being accessed. If the value never gets accessed, no exception will be thrown.
Placeholders for which no value is provided will be replaced with an empty string.
"Hello, ${MissingValue}"
will result in "Hello, "
Placeholders for which no value is provided will not be replaced. The resulting value will still contain the placeholder.
"Hello, ${MissingValue}"
will result in "Hello, ${MissingValue}"
You can find some examples using ConfigurationPlaceholders here
Resolves placeholder values from an in-memory lookup. Works similar like the AddInMemoryCollection
configuration source.
new InMemoryPlaceholderResolver( new Dictionary<String, String?>
{
{ "ApplicationName", Assembly.GetExecutingAssembly().GetName().Name },
{ "ApplicationVersion", Assembly.GetExecutingAssembly().GetName().Version!.ToString() }
} )
Resolves placeholder values by invoking user provided value factories.
new CallbackPlaceholderResolver( new Dictionary<String, Func<String?>>
{
{ "Time", () => DateTime.Now.ToString( "HH:mm:ss.fff" ) }
} )
Searches for values in all configuration sources matching the placeholder key.
new ConfigurationPlaceholderResolver()
In this example LocalDb
is build based on other values in appsettings.json
:
{
"Lookup": {
"DataDir": "X:/Temp/",
"DbDir": "${Lookup:DataDir}db/"
},
"LocalDb": "${Lookup:DbDir}store.db"
}
Resolves placeholder values by searching for environment variables matching the placeholder key. The search is performed in this priority order:
- EnvironmentVariableTarget.Process
- EnvironmentVariableTarget.User
- EnvironmentVariableTarget.Machine
new EnvironmentVariableResolver()
You can add your own placeholder resolvers by implementing IPlaceholderResolver
.
Potential sources could be REST APIs, files, secret stores etc...
Different application setups require different ways to add ConfigurationPlaceholders.
var builder = WebApplication.CreateBuilder( args );
builder
.AddConfigurationPlaceholders( new InMemoryPlaceholderResolver( new Dictionary<String, String?>
{
{ "FQDN", fullDomainName }
} ) );
Host
.CreateDefaultBuilder( args )
.AddConfigurationPlaceholders( new InMemoryPlaceholderResolver( new Dictionary<String, String?>
{
{ "FQDN", fullDomainName }
} ) )
var configuration = new ConfigurationBuilder()
.AddJsonFile( "appsettings.json" )
....
.AddConfigurationPlaceholders( new List<IPlaceholderResolver>
{
new InMemoryPlaceholderResolver( new Dictionary<String, String?>
{
{
"ApplicationName", Assembly.GetExecutingAssembly().GetName().Name
}
}
} ),
new EnvironmentVariableResolver()
} )
.Build();
You can reference values containing placeholders from placeholders...
{
"Lookup": {
"SinksNs": "Serilog.Sinks",
"DataDir": "X:/Temp/",
"LogDir": "${Lookup:DataDir}logs/",
"DbDir": "${Lookup:DataDir}db/"
},
"Serilog": {
"Using": [ "${Lookup:SinksNs}.Console", "${Lookup:SinksNs}.File" ],
"MinimumLevel": "Debug",
"WriteTo": [
{ "Name": "Console" },
{
"Name": "File",
"Args": { "path": "${Lookup:LogDir}${ApplicationName}/${ApplicationName}-${ApplicationVersion}.log" }
}
]
},
"Test": "Today is the ${Today} (${Day}) an it is ${Time} ${NoValueDefinedForThisOne}",
"LocalDb": "${Lookup:DbDir}store.db"
}
In this example we can see several placeholders referencing values containing other placeholders.
E.g. ${Lookup:DbDir}
will be resolved with the value ${Lookup:DataDir}db/
from Lookup:DbDir
(using ConfigurationPlaceholderResolver
). ${Lookup:DataDir}
is another placeholder which will be replaced with the value of Lookup:DataDir
=> X:/Temp/
.
You can combine values from multiple IPlaceholderResolver
with multiple configuration sources.