Skip to content

Repository files navigation

DotNet.MultiSourceConfiguration

NuGet VersionBuild Status

Configuration library with multiple sources for .NET.

Why DotNet.MultiSourceConfiguration

A very typical scenario in microservices (which typically run in containers) is to configure a service via a configuration file, and overwrite that configuration with whatever you can find in environment variables and command line. Used to Spring Boot's approach to configuration based in properties and property sources, I have struggled to find a simple library in .NET allowing to read configuration from different sources and overwrite it in a specified source order.

The Microsoft.Extensions.Configuration project follow a very similar approach but have some drawbacks:

  • It has a huge amount of dependencies.
  • At the moment of writing DotNet.MultiSourceConfiguration, the existing documentation was outdated and did not work with the last version of the library.

How to use it

The approach followed by DotNet.MultiSourceConfiguration is the population of configuration classes, that can subsequently be registered on an IOC container or made avaialable as a static property. The properties of the configuration class must be decorated with the Property attribute, indicating the name of the configuration property that must be mapped to the class property:

publicclassTestConfigurationDto{// By default properties are not required[Property("test.int.property")]publicint?IntProperty{get;set;}// The required condition of a property can be explicitly included.// If no value is provided InvalidOperationException is thrown.[Property("test.string.property",Required=true)]publicstringStringProperty{get;set;}// Properties can be marked as not required. The default value of the// given type converter will be applied (typically, null).[Property("test.long.property",Required=false)]publiclong?LongProperty{get;set;}// The "Default" property can be used to provide a default value in// case it is not provided via configuration.[Property("test.bool.property",Default="true")]publicbool?BoolProperty{get;set;}}

Configuration classes are populated via a configuration builder, which can be specified a series of sources:

classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

When adding sources to a configuration builder a priority can be specified. Higher priority numbers indicate higher priority (that is, the configuration provided by those sources will overwrite the configuration read from sources with lower priority). Given the same priority, sources will be evaluated in the order they are provided:

  • The properties provided by the first configuration source will be read.
  • The properties provided by the second confiration source will be read, overwriting whatever was read from previous sources, and so on.
classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();// Configuration is first read from configuration fileconfigurationBuilder.AddSources(1,newAppSettingsSource());// Configuration is then read form environment variables, overwriting the values read from app settings.configurationBuilder.AddSources(2,newEnvironmentVariableSource());// Configuration is finally read from command line arguments, overwriting the values read from app settings and environment variablesconfigurationBuilder.AddSources(3,newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

The configuration builder implements the IConfigurationBuilder interface. This way, the configuration builder is easier to mock in unit tests of classes that depend on it. A common pattern is to register the configuration builder in an IoC container and inject it in classes that need it.

The configuration builder has caching capabilities, allowing to re-use built configuration classes until a configurable cache expiration times out. When the cache expires and a configuration class is re-built, then the configuration is re-read from the sources. The cache expiration is configurable via the CacheExpiration property (by default the cache expiration is 0, i.e. no caching is done):

IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));configurationBuilder.CacheExpiration=TimeSpan.FromMinutes(2);

Properties

Properties in configuration objects that you want to populate with DotNet.MultiSourceConfiguration must be decorated with the Property annotation. This annotation receives the name of the configuration property that must be read from configuration in order to set the value to the configuration object property.

publicclassTestConfigurationDto{[Property("test.int.property")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("test.int.property","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.IntProperty);}

The Property annotation accepts the following attributes:

  • Required (bool): When set to true, the corresponding configuration property will be mandatory. An InvalidOperationException will be thrown if it is not possible to read the property value from at least one configuration source. Configuration properties are not required by default.
  • Default (string): A default value to set to the configuration property in case the it was not possible to read the property value from any configuration source.

Properties in the configuration object that are not decorated with the Property annotation will not be populated by default.

publicclassTestConfigurationDto{// By default only properties decorated with the Property annotation are populated[Property("test.int.property")]publicint?IntProperty{get;set;}// This property will be ignored, unless HandleNonDecoratedProperties is set to truepublicint?IgnoredProperty{get;set;}}

In case you want DotNet.MultiSourceConfiguration to also populate non-decorated configuration object properties you may set the HandleNonDecoratedProperties property of IConfigurationBuilder to true:

configurationBuilder.HandleNonDecoratedProperties=true;

In this case, the name of the configuration object property itself will be used as property name when retrieving the configuration from the configuration sources.

publicclassTestConfigurationDto{publicintNonDecoratedProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("NonDecoratedProperty","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);configurationBuilder.HandleNonDecoratedProperties=true;TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.NonDecoratedProperty);}

This behavior is useful when you are introducing DotNet.MultiSourceConfiguration in an already existing application that has a big number of configuration objects.

Property Prefixes

It is possible to indicate a prefix when building a configuration object in the IConfigurationBuilder.Build<T>(string propertiesPrefix) function. This will add the specified prefix to each one of the property names when trying to find the property value in the different configuration sources:

publicclassTestConfigurationDto{[Property("testProperty")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("myComponent1.testProperty","1");memoryConfigurationSource.Add("myComponent2.testProperty","2");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto1=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent1.");TestConfigurationDtoconfigurationDto2=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent2.");Assert.AreEqual(1,configurationDto1.IntProperty);Assert.AreEqual(2,configurationDto2.IntProperty);}

This is useful when you want to reuse the same configuration object in different contexts, each one having a different prefix.

Property Sources

The available, out-of-the-box, property sources are:

  • AppSettingsSource: looks for the properties in the .NET application settings file.
  • EnvironmentVariableSource: looks for the properties in the system environment variables.
  • CommandLineSource: tries to match the properties with arguments in the command line, with the format --<property>=<value>.
  • MemorySource: allows to define a series of properties in memory as use them as source of configuration.

There are some additional projects that provide property sources for some common configuration services, like DotNet.MultisourceConfiguration.Zookeeper, that provides a configuration source for ZooKeeper.

In addition to these property sources you can implement your own by providing implementations of the IStringConfigSource interface:

publicinterfaceIStringConfigSource{TimeSpanCacheExpiration{set;}boolTryGetString(stringproperty,outstringvalue);}

The configuration properties are overwritten by the given property sources in the order they are specified in the AddSources call. In the example above, the properties will be first read in the application settings file. Subsequently they will be overwritten with the properties found in environment variables (in case they are found). Finally, the properties will be overwritten with the values found in command-line arguments.

This provides a very convenient deployment behavior (specially for applications running in containers), in which applications take some default configuration from application settings, that is overwritten by the environment variables set in the machine (or container) and are finally overwritten with whatever has been provided in command line arguments.

It is also possible to perform several calls to AddSources. The given configuration sources are appended to the already existing list. This way, the new provided sources will overwrite configuration properties already set by configuration sources set in previous cllas to AddSources.

Property Types

The following types for configuration properties are available:

  • bool, bool?, bool[], List<bool>, IEnumerable<bool>
  • string, string[], List<string>, IEnumerable<string>
  • int, int?, int[], List<int>, IEnumerable<int>
  • long, long?, long[], List<long>, IEnumerable<long>
  • decimal, decimal?, decimal[], List<decimal>, IEnumerable<decimal>
  • float, float?, float[], List<float>, IEnumerable<float>
  • double, double?, double[], List<double>, IEnumerable<double>

In addition to these types, you can add your own type converters by providing implementations of the ITypeConverter interface to the AddTypeConverter<T>() method of ConfigurationBuilder. For convenience, the LambdaConverter is provided, that makes it easier to implement your own type converter:

configurationBuilder.AddTypeConverter(newLambdaConverter<MyType>(null/* Default value */, s =>MyType.Parse(s)/* Converter lambda */));

About

Configuration library with multiple sources for .NET

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - rubms/DotNet.MultiSourceConfiguration: Configuration library with multiple sources for .NET · GitHub
Skip to content

Repository files navigation

DotNet.MultiSourceConfiguration

NuGet VersionBuild Status

Configuration library with multiple sources for .NET.

Why DotNet.MultiSourceConfiguration

A very typical scenario in microservices (which typically run in containers) is to configure a service via a configuration file, and overwrite that configuration with whatever you can find in environment variables and command line. Used to Spring Boot's approach to configuration based in properties and property sources, I have struggled to find a simple library in .NET allowing to read configuration from different sources and overwrite it in a specified source order.

The Microsoft.Extensions.Configuration project follow a very similar approach but have some drawbacks:

  • It has a huge amount of dependencies.
  • At the moment of writing DotNet.MultiSourceConfiguration, the existing documentation was outdated and did not work with the last version of the library.

How to use it

The approach followed by DotNet.MultiSourceConfiguration is the population of configuration classes, that can subsequently be registered on an IOC container or made avaialable as a static property. The properties of the configuration class must be decorated with the Property attribute, indicating the name of the configuration property that must be mapped to the class property:

publicclassTestConfigurationDto{// By default properties are not required[Property("test.int.property")]publicint?IntProperty{get;set;}// The required condition of a property can be explicitly included.// If no value is provided InvalidOperationException is thrown.[Property("test.string.property",Required=true)]publicstringStringProperty{get;set;}// Properties can be marked as not required. The default value of the// given type converter will be applied (typically, null).[Property("test.long.property",Required=false)]publiclong?LongProperty{get;set;}// The "Default" property can be used to provide a default value in// case it is not provided via configuration.[Property("test.bool.property",Default="true")]publicbool?BoolProperty{get;set;}}

Configuration classes are populated via a configuration builder, which can be specified a series of sources:

classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

When adding sources to a configuration builder a priority can be specified. Higher priority numbers indicate higher priority (that is, the configuration provided by those sources will overwrite the configuration read from sources with lower priority). Given the same priority, sources will be evaluated in the order they are provided:

  • The properties provided by the first configuration source will be read.
  • The properties provided by the second confiration source will be read, overwriting whatever was read from previous sources, and so on.
classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();// Configuration is first read from configuration fileconfigurationBuilder.AddSources(1,newAppSettingsSource());// Configuration is then read form environment variables, overwriting the values read from app settings.configurationBuilder.AddSources(2,newEnvironmentVariableSource());// Configuration is finally read from command line arguments, overwriting the values read from app settings and environment variablesconfigurationBuilder.AddSources(3,newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

The configuration builder implements the IConfigurationBuilder interface. This way, the configuration builder is easier to mock in unit tests of classes that depend on it. A common pattern is to register the configuration builder in an IoC container and inject it in classes that need it.

The configuration builder has caching capabilities, allowing to re-use built configuration classes until a configurable cache expiration times out. When the cache expires and a configuration class is re-built, then the configuration is re-read from the sources. The cache expiration is configurable via the CacheExpiration property (by default the cache expiration is 0, i.e. no caching is done):

IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));configurationBuilder.CacheExpiration=TimeSpan.FromMinutes(2);

Properties

Properties in configuration objects that you want to populate with DotNet.MultiSourceConfiguration must be decorated with the Property annotation. This annotation receives the name of the configuration property that must be read from configuration in order to set the value to the configuration object property.

publicclassTestConfigurationDto{[Property("test.int.property")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("test.int.property","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.IntProperty);}

The Property annotation accepts the following attributes:

  • Required (bool): When set to true, the corresponding configuration property will be mandatory. An InvalidOperationException will be thrown if it is not possible to read the property value from at least one configuration source. Configuration properties are not required by default.
  • Default (string): A default value to set to the configuration property in case the it was not possible to read the property value from any configuration source.

Properties in the configuration object that are not decorated with the Property annotation will not be populated by default.

publicclassTestConfigurationDto{// By default only properties decorated with the Property annotation are populated[Property("test.int.property")]publicint?IntProperty{get;set;}// This property will be ignored, unless HandleNonDecoratedProperties is set to truepublicint?IgnoredProperty{get;set;}}

In case you want DotNet.MultiSourceConfiguration to also populate non-decorated configuration object properties you may set the HandleNonDecoratedProperties property of IConfigurationBuilder to true:

configurationBuilder.HandleNonDecoratedProperties=true;

In this case, the name of the configuration object property itself will be used as property name when retrieving the configuration from the configuration sources.

publicclassTestConfigurationDto{publicintNonDecoratedProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("NonDecoratedProperty","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);configurationBuilder.HandleNonDecoratedProperties=true;TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.NonDecoratedProperty);}

This behavior is useful when you are introducing DotNet.MultiSourceConfiguration in an already existing application that has a big number of configuration objects.

Property Prefixes

It is possible to indicate a prefix when building a configuration object in the IConfigurationBuilder.Build<T>(string propertiesPrefix) function. This will add the specified prefix to each one of the property names when trying to find the property value in the different configuration sources:

publicclassTestConfigurationDto{[Property("testProperty")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("myComponent1.testProperty","1");memoryConfigurationSource.Add("myComponent2.testProperty","2");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto1=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent1.");TestConfigurationDtoconfigurationDto2=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent2.");Assert.AreEqual(1,configurationDto1.IntProperty);Assert.AreEqual(2,configurationDto2.IntProperty);}

This is useful when you want to reuse the same configuration object in different contexts, each one having a different prefix.

Property Sources

The available, out-of-the-box, property sources are:

  • AppSettingsSource: looks for the properties in the .NET application settings file.
  • EnvironmentVariableSource: looks for the properties in the system environment variables.
  • CommandLineSource: tries to match the properties with arguments in the command line, with the format --<property>=<value>.
  • MemorySource: allows to define a series of properties in memory as use them as source of configuration.

There are some additional projects that provide property sources for some common configuration services, like DotNet.MultisourceConfiguration.Zookeeper, that provides a configuration source for ZooKeeper.

In addition to these property sources you can implement your own by providing implementations of the IStringConfigSource interface:

publicinterfaceIStringConfigSource{TimeSpanCacheExpiration{set;}boolTryGetString(stringproperty,outstringvalue);}

The configuration properties are overwritten by the given property sources in the order they are specified in the AddSources call. In the example above, the properties will be first read in the application settings file. Subsequently they will be overwritten with the properties found in environment variables (in case they are found). Finally, the properties will be overwritten with the values found in command-line arguments.

This provides a very convenient deployment behavior (specially for applications running in containers), in which applications take some default configuration from application settings, that is overwritten by the environment variables set in the machine (or container) and are finally overwritten with whatever has been provided in command line arguments.

It is also possible to perform several calls to AddSources. The given configuration sources are appended to the already existing list. This way, the new provided sources will overwrite configuration properties already set by configuration sources set in previous cllas to AddSources.

Property Types

The following types for configuration properties are available:

  • bool, bool?, bool[], List<bool>, IEnumerable<bool>
  • string, string[], List<string>, IEnumerable<string>
  • int, int?, int[], List<int>, IEnumerable<int>
  • long, long?, long[], List<long>, IEnumerable<long>
  • decimal, decimal?, decimal[], List<decimal>, IEnumerable<decimal>
  • float, float?, float[], List<float>, IEnumerable<float>
  • double, double?, double[], List<double>, IEnumerable<double>

In addition to these types, you can add your own type converters by providing implementations of the ITypeConverter interface to the AddTypeConverter<T>() method of ConfigurationBuilder. For convenience, the LambdaConverter is provided, that makes it easier to implement your own type converter:

configurationBuilder.AddTypeConverter(newLambdaConverter<MyType>(null/* Default value */, s =>MyType.Parse(s)/* Converter lambda */));

About

Configuration library with multiple sources for .NET

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - rubms/DotNet.MultiSourceConfiguration: Configuration library with multiple sources for .NET · GitHub
Skip to content

Repository files navigation

DotNet.MultiSourceConfiguration

NuGet VersionBuild Status

Configuration library with multiple sources for .NET.

Why DotNet.MultiSourceConfiguration

A very typical scenario in microservices (which typically run in containers) is to configure a service via a configuration file, and overwrite that configuration with whatever you can find in environment variables and command line. Used to Spring Boot's approach to configuration based in properties and property sources, I have struggled to find a simple library in .NET allowing to read configuration from different sources and overwrite it in a specified source order.

The Microsoft.Extensions.Configuration project follow a very similar approach but have some drawbacks:

  • It has a huge amount of dependencies.
  • At the moment of writing DotNet.MultiSourceConfiguration, the existing documentation was outdated and did not work with the last version of the library.

How to use it

The approach followed by DotNet.MultiSourceConfiguration is the population of configuration classes, that can subsequently be registered on an IOC container or made avaialable as a static property. The properties of the configuration class must be decorated with the Property attribute, indicating the name of the configuration property that must be mapped to the class property:

publicclassTestConfigurationDto{// By default properties are not required[Property("test.int.property")]publicint?IntProperty{get;set;}// The required condition of a property can be explicitly included.// If no value is provided InvalidOperationException is thrown.[Property("test.string.property",Required=true)]publicstringStringProperty{get;set;}// Properties can be marked as not required. The default value of the// given type converter will be applied (typically, null).[Property("test.long.property",Required=false)]publiclong?LongProperty{get;set;}// The "Default" property can be used to provide a default value in// case it is not provided via configuration.[Property("test.bool.property",Default="true")]publicbool?BoolProperty{get;set;}}

Configuration classes are populated via a configuration builder, which can be specified a series of sources:

classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

When adding sources to a configuration builder a priority can be specified. Higher priority numbers indicate higher priority (that is, the configuration provided by those sources will overwrite the configuration read from sources with lower priority). Given the same priority, sources will be evaluated in the order they are provided:

  • The properties provided by the first configuration source will be read.
  • The properties provided by the second confiration source will be read, overwriting whatever was read from previous sources, and so on.
classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();// Configuration is first read from configuration fileconfigurationBuilder.AddSources(1,newAppSettingsSource());// Configuration is then read form environment variables, overwriting the values read from app settings.configurationBuilder.AddSources(2,newEnvironmentVariableSource());// Configuration is finally read from command line arguments, overwriting the values read from app settings and environment variablesconfigurationBuilder.AddSources(3,newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

The configuration builder implements the IConfigurationBuilder interface. This way, the configuration builder is easier to mock in unit tests of classes that depend on it. A common pattern is to register the configuration builder in an IoC container and inject it in classes that need it.

The configuration builder has caching capabilities, allowing to re-use built configuration classes until a configurable cache expiration times out. When the cache expires and a configuration class is re-built, then the configuration is re-read from the sources. The cache expiration is configurable via the CacheExpiration property (by default the cache expiration is 0, i.e. no caching is done):

IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));configurationBuilder.CacheExpiration=TimeSpan.FromMinutes(2);

Properties

Properties in configuration objects that you want to populate with DotNet.MultiSourceConfiguration must be decorated with the Property annotation. This annotation receives the name of the configuration property that must be read from configuration in order to set the value to the configuration object property.

publicclassTestConfigurationDto{[Property("test.int.property")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("test.int.property","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.IntProperty);}

The Property annotation accepts the following attributes:

  • Required (bool): When set to true, the corresponding configuration property will be mandatory. An InvalidOperationException will be thrown if it is not possible to read the property value from at least one configuration source. Configuration properties are not required by default.
  • Default (string): A default value to set to the configuration property in case the it was not possible to read the property value from any configuration source.

Properties in the configuration object that are not decorated with the Property annotation will not be populated by default.

publicclassTestConfigurationDto{// By default only properties decorated with the Property annotation are populated[Property("test.int.property")]publicint?IntProperty{get;set;}// This property will be ignored, unless HandleNonDecoratedProperties is set to truepublicint?IgnoredProperty{get;set;}}

In case you want DotNet.MultiSourceConfiguration to also populate non-decorated configuration object properties you may set the HandleNonDecoratedProperties property of IConfigurationBuilder to true:

configurationBuilder.HandleNonDecoratedProperties=true;

In this case, the name of the configuration object property itself will be used as property name when retrieving the configuration from the configuration sources.

publicclassTestConfigurationDto{publicintNonDecoratedProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("NonDecoratedProperty","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);configurationBuilder.HandleNonDecoratedProperties=true;TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.NonDecoratedProperty);}

This behavior is useful when you are introducing DotNet.MultiSourceConfiguration in an already existing application that has a big number of configuration objects.

Property Prefixes

It is possible to indicate a prefix when building a configuration object in the IConfigurationBuilder.Build<T>(string propertiesPrefix) function. This will add the specified prefix to each one of the property names when trying to find the property value in the different configuration sources:

publicclassTestConfigurationDto{[Property("testProperty")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("myComponent1.testProperty","1");memoryConfigurationSource.Add("myComponent2.testProperty","2");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto1=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent1.");TestConfigurationDtoconfigurationDto2=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent2.");Assert.AreEqual(1,configurationDto1.IntProperty);Assert.AreEqual(2,configurationDto2.IntProperty);}

This is useful when you want to reuse the same configuration object in different contexts, each one having a different prefix.

Property Sources

The available, out-of-the-box, property sources are:

  • AppSettingsSource: looks for the properties in the .NET application settings file.
  • EnvironmentVariableSource: looks for the properties in the system environment variables.
  • CommandLineSource: tries to match the properties with arguments in the command line, with the format --<property>=<value>.
  • MemorySource: allows to define a series of properties in memory as use them as source of configuration.

There are some additional projects that provide property sources for some common configuration services, like DotNet.MultisourceConfiguration.Zookeeper, that provides a configuration source for ZooKeeper.

In addition to these property sources you can implement your own by providing implementations of the IStringConfigSource interface:

publicinterfaceIStringConfigSource{TimeSpanCacheExpiration{set;}boolTryGetString(stringproperty,outstringvalue);}

The configuration properties are overwritten by the given property sources in the order they are specified in the AddSources call. In the example above, the properties will be first read in the application settings file. Subsequently they will be overwritten with the properties found in environment variables (in case they are found). Finally, the properties will be overwritten with the values found in command-line arguments.

This provides a very convenient deployment behavior (specially for applications running in containers), in which applications take some default configuration from application settings, that is overwritten by the environment variables set in the machine (or container) and are finally overwritten with whatever has been provided in command line arguments.

It is also possible to perform several calls to AddSources. The given configuration sources are appended to the already existing list. This way, the new provided sources will overwrite configuration properties already set by configuration sources set in previous cllas to AddSources.

Property Types

The following types for configuration properties are available:

  • bool, bool?, bool[], List<bool>, IEnumerable<bool>
  • string, string[], List<string>, IEnumerable<string>
  • int, int?, int[], List<int>, IEnumerable<int>
  • long, long?, long[], List<long>, IEnumerable<long>
  • decimal, decimal?, decimal[], List<decimal>, IEnumerable<decimal>
  • float, float?, float[], List<float>, IEnumerable<float>
  • double, double?, double[], List<double>, IEnumerable<double>

In addition to these types, you can add your own type converters by providing implementations of the ITypeConverter interface to the AddTypeConverter<T>() method of ConfigurationBuilder. For convenience, the LambdaConverter is provided, that makes it easier to implement your own type converter:

configurationBuilder.AddTypeConverter(newLambdaConverter<MyType>(null/* Default value */, s =>MyType.Parse(s)/* Converter lambda */));

About

Configuration library with multiple sources for .NET

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - rubms/DotNet.MultiSourceConfiguration: Configuration library with multiple sources for .NET · GitHub
Skip to content

Repository files navigation

DotNet.MultiSourceConfiguration

NuGet VersionBuild Status

Configuration library with multiple sources for .NET.

Why DotNet.MultiSourceConfiguration

A very typical scenario in microservices (which typically run in containers) is to configure a service via a configuration file, and overwrite that configuration with whatever you can find in environment variables and command line. Used to Spring Boot's approach to configuration based in properties and property sources, I have struggled to find a simple library in .NET allowing to read configuration from different sources and overwrite it in a specified source order.

The Microsoft.Extensions.Configuration project follow a very similar approach but have some drawbacks:

  • It has a huge amount of dependencies.
  • At the moment of writing DotNet.MultiSourceConfiguration, the existing documentation was outdated and did not work with the last version of the library.

How to use it

The approach followed by DotNet.MultiSourceConfiguration is the population of configuration classes, that can subsequently be registered on an IOC container or made avaialable as a static property. The properties of the configuration class must be decorated with the Property attribute, indicating the name of the configuration property that must be mapped to the class property:

publicclassTestConfigurationDto{// By default properties are not required[Property("test.int.property")]publicint?IntProperty{get;set;}// The required condition of a property can be explicitly included.// If no value is provided InvalidOperationException is thrown.[Property("test.string.property",Required=true)]publicstringStringProperty{get;set;}// Properties can be marked as not required. The default value of the// given type converter will be applied (typically, null).[Property("test.long.property",Required=false)]publiclong?LongProperty{get;set;}// The "Default" property can be used to provide a default value in// case it is not provided via configuration.[Property("test.bool.property",Default="true")]publicbool?BoolProperty{get;set;}}

Configuration classes are populated via a configuration builder, which can be specified a series of sources:

classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

When adding sources to a configuration builder a priority can be specified. Higher priority numbers indicate higher priority (that is, the configuration provided by those sources will overwrite the configuration read from sources with lower priority). Given the same priority, sources will be evaluated in the order they are provided:

  • The properties provided by the first configuration source will be read.
  • The properties provided by the second confiration source will be read, overwriting whatever was read from previous sources, and so on.
classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();// Configuration is first read from configuration fileconfigurationBuilder.AddSources(1,newAppSettingsSource());// Configuration is then read form environment variables, overwriting the values read from app settings.configurationBuilder.AddSources(2,newEnvironmentVariableSource());// Configuration is finally read from command line arguments, overwriting the values read from app settings and environment variablesconfigurationBuilder.AddSources(3,newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

The configuration builder implements the IConfigurationBuilder interface. This way, the configuration builder is easier to mock in unit tests of classes that depend on it. A common pattern is to register the configuration builder in an IoC container and inject it in classes that need it.

The configuration builder has caching capabilities, allowing to re-use built configuration classes until a configurable cache expiration times out. When the cache expires and a configuration class is re-built, then the configuration is re-read from the sources. The cache expiration is configurable via the CacheExpiration property (by default the cache expiration is 0, i.e. no caching is done):

IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));configurationBuilder.CacheExpiration=TimeSpan.FromMinutes(2);

Properties

Properties in configuration objects that you want to populate with DotNet.MultiSourceConfiguration must be decorated with the Property annotation. This annotation receives the name of the configuration property that must be read from configuration in order to set the value to the configuration object property.

publicclassTestConfigurationDto{[Property("test.int.property")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("test.int.property","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.IntProperty);}

The Property annotation accepts the following attributes:

  • Required (bool): When set to true, the corresponding configuration property will be mandatory. An InvalidOperationException will be thrown if it is not possible to read the property value from at least one configuration source. Configuration properties are not required by default.
  • Default (string): A default value to set to the configuration property in case the it was not possible to read the property value from any configuration source.

Properties in the configuration object that are not decorated with the Property annotation will not be populated by default.

publicclassTestConfigurationDto{// By default only properties decorated with the Property annotation are populated[Property("test.int.property")]publicint?IntProperty{get;set;}// This property will be ignored, unless HandleNonDecoratedProperties is set to truepublicint?IgnoredProperty{get;set;}}

In case you want DotNet.MultiSourceConfiguration to also populate non-decorated configuration object properties you may set the HandleNonDecoratedProperties property of IConfigurationBuilder to true:

configurationBuilder.HandleNonDecoratedProperties=true;

In this case, the name of the configuration object property itself will be used as property name when retrieving the configuration from the configuration sources.

publicclassTestConfigurationDto{publicintNonDecoratedProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("NonDecoratedProperty","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);configurationBuilder.HandleNonDecoratedProperties=true;TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.NonDecoratedProperty);}

This behavior is useful when you are introducing DotNet.MultiSourceConfiguration in an already existing application that has a big number of configuration objects.

Property Prefixes

It is possible to indicate a prefix when building a configuration object in the IConfigurationBuilder.Build<T>(string propertiesPrefix) function. This will add the specified prefix to each one of the property names when trying to find the property value in the different configuration sources:

publicclassTestConfigurationDto{[Property("testProperty")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("myComponent1.testProperty","1");memoryConfigurationSource.Add("myComponent2.testProperty","2");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto1=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent1.");TestConfigurationDtoconfigurationDto2=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent2.");Assert.AreEqual(1,configurationDto1.IntProperty);Assert.AreEqual(2,configurationDto2.IntProperty);}

This is useful when you want to reuse the same configuration object in different contexts, each one having a different prefix.

Property Sources

The available, out-of-the-box, property sources are:

  • AppSettingsSource: looks for the properties in the .NET application settings file.
  • EnvironmentVariableSource: looks for the properties in the system environment variables.
  • CommandLineSource: tries to match the properties with arguments in the command line, with the format --<property>=<value>.
  • MemorySource: allows to define a series of properties in memory as use them as source of configuration.

There are some additional projects that provide property sources for some common configuration services, like DotNet.MultisourceConfiguration.Zookeeper, that provides a configuration source for ZooKeeper.

In addition to these property sources you can implement your own by providing implementations of the IStringConfigSource interface:

publicinterfaceIStringConfigSource{TimeSpanCacheExpiration{set;}boolTryGetString(stringproperty,outstringvalue);}

The configuration properties are overwritten by the given property sources in the order they are specified in the AddSources call. In the example above, the properties will be first read in the application settings file. Subsequently they will be overwritten with the properties found in environment variables (in case they are found). Finally, the properties will be overwritten with the values found in command-line arguments.

This provides a very convenient deployment behavior (specially for applications running in containers), in which applications take some default configuration from application settings, that is overwritten by the environment variables set in the machine (or container) and are finally overwritten with whatever has been provided in command line arguments.

It is also possible to perform several calls to AddSources. The given configuration sources are appended to the already existing list. This way, the new provided sources will overwrite configuration properties already set by configuration sources set in previous cllas to AddSources.

Property Types

The following types for configuration properties are available:

  • bool, bool?, bool[], List<bool>, IEnumerable<bool>
  • string, string[], List<string>, IEnumerable<string>
  • int, int?, int[], List<int>, IEnumerable<int>
  • long, long?, long[], List<long>, IEnumerable<long>
  • decimal, decimal?, decimal[], List<decimal>, IEnumerable<decimal>
  • float, float?, float[], List<float>, IEnumerable<float>
  • double, double?, double[], List<double>, IEnumerable<double>

In addition to these types, you can add your own type converters by providing implementations of the ITypeConverter interface to the AddTypeConverter<T>() method of ConfigurationBuilder. For convenience, the LambdaConverter is provided, that makes it easier to implement your own type converter:

configurationBuilder.AddTypeConverter(newLambdaConverter<MyType>(null/* Default value */, s =>MyType.Parse(s)/* Converter lambda */));

About

Configuration library with multiple sources for .NET

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - rubms/DotNet.MultiSourceConfiguration: Configuration library with multiple sources for .NET · GitHub
Skip to content

Repository files navigation

DotNet.MultiSourceConfiguration

NuGet VersionBuild Status

Configuration library with multiple sources for .NET.

Why DotNet.MultiSourceConfiguration

A very typical scenario in microservices (which typically run in containers) is to configure a service via a configuration file, and overwrite that configuration with whatever you can find in environment variables and command line. Used to Spring Boot's approach to configuration based in properties and property sources, I have struggled to find a simple library in .NET allowing to read configuration from different sources and overwrite it in a specified source order.

The Microsoft.Extensions.Configuration project follow a very similar approach but have some drawbacks:

  • It has a huge amount of dependencies.
  • At the moment of writing DotNet.MultiSourceConfiguration, the existing documentation was outdated and did not work with the last version of the library.

How to use it

The approach followed by DotNet.MultiSourceConfiguration is the population of configuration classes, that can subsequently be registered on an IOC container or made avaialable as a static property. The properties of the configuration class must be decorated with the Property attribute, indicating the name of the configuration property that must be mapped to the class property:

publicclassTestConfigurationDto{// By default properties are not required[Property("test.int.property")]publicint?IntProperty{get;set;}// The required condition of a property can be explicitly included.// If no value is provided InvalidOperationException is thrown.[Property("test.string.property",Required=true)]publicstringStringProperty{get;set;}// Properties can be marked as not required. The default value of the// given type converter will be applied (typically, null).[Property("test.long.property",Required=false)]publiclong?LongProperty{get;set;}// The "Default" property can be used to provide a default value in// case it is not provided via configuration.[Property("test.bool.property",Default="true")]publicbool?BoolProperty{get;set;}}

Configuration classes are populated via a configuration builder, which can be specified a series of sources:

classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

When adding sources to a configuration builder a priority can be specified. Higher priority numbers indicate higher priority (that is, the configuration provided by those sources will overwrite the configuration read from sources with lower priority). Given the same priority, sources will be evaluated in the order they are provided:

  • The properties provided by the first configuration source will be read.
  • The properties provided by the second confiration source will be read, overwriting whatever was read from previous sources, and so on.
classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();// Configuration is first read from configuration fileconfigurationBuilder.AddSources(1,newAppSettingsSource());// Configuration is then read form environment variables, overwriting the values read from app settings.configurationBuilder.AddSources(2,newEnvironmentVariableSource());// Configuration is finally read from command line arguments, overwriting the values read from app settings and environment variablesconfigurationBuilder.AddSources(3,newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

The configuration builder implements the IConfigurationBuilder interface. This way, the configuration builder is easier to mock in unit tests of classes that depend on it. A common pattern is to register the configuration builder in an IoC container and inject it in classes that need it.

The configuration builder has caching capabilities, allowing to re-use built configuration classes until a configurable cache expiration times out. When the cache expires and a configuration class is re-built, then the configuration is re-read from the sources. The cache expiration is configurable via the CacheExpiration property (by default the cache expiration is 0, i.e. no caching is done):

IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));configurationBuilder.CacheExpiration=TimeSpan.FromMinutes(2);

Properties

Properties in configuration objects that you want to populate with DotNet.MultiSourceConfiguration must be decorated with the Property annotation. This annotation receives the name of the configuration property that must be read from configuration in order to set the value to the configuration object property.

publicclassTestConfigurationDto{[Property("test.int.property")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("test.int.property","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.IntProperty);}

The Property annotation accepts the following attributes:

  • Required (bool): When set to true, the corresponding configuration property will be mandatory. An InvalidOperationException will be thrown if it is not possible to read the property value from at least one configuration source. Configuration properties are not required by default.
  • Default (string): A default value to set to the configuration property in case the it was not possible to read the property value from any configuration source.

Properties in the configuration object that are not decorated with the Property annotation will not be populated by default.

publicclassTestConfigurationDto{// By default only properties decorated with the Property annotation are populated[Property("test.int.property")]publicint?IntProperty{get;set;}// This property will be ignored, unless HandleNonDecoratedProperties is set to truepublicint?IgnoredProperty{get;set;}}

In case you want DotNet.MultiSourceConfiguration to also populate non-decorated configuration object properties you may set the HandleNonDecoratedProperties property of IConfigurationBuilder to true:

configurationBuilder.HandleNonDecoratedProperties=true;

In this case, the name of the configuration object property itself will be used as property name when retrieving the configuration from the configuration sources.

publicclassTestConfigurationDto{publicintNonDecoratedProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("NonDecoratedProperty","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);configurationBuilder.HandleNonDecoratedProperties=true;TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.NonDecoratedProperty);}

This behavior is useful when you are introducing DotNet.MultiSourceConfiguration in an already existing application that has a big number of configuration objects.

Property Prefixes

It is possible to indicate a prefix when building a configuration object in the IConfigurationBuilder.Build<T>(string propertiesPrefix) function. This will add the specified prefix to each one of the property names when trying to find the property value in the different configuration sources:

publicclassTestConfigurationDto{[Property("testProperty")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("myComponent1.testProperty","1");memoryConfigurationSource.Add("myComponent2.testProperty","2");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto1=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent1.");TestConfigurationDtoconfigurationDto2=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent2.");Assert.AreEqual(1,configurationDto1.IntProperty);Assert.AreEqual(2,configurationDto2.IntProperty);}

This is useful when you want to reuse the same configuration object in different contexts, each one having a different prefix.

Property Sources

The available, out-of-the-box, property sources are:

  • AppSettingsSource: looks for the properties in the .NET application settings file.
  • EnvironmentVariableSource: looks for the properties in the system environment variables.
  • CommandLineSource: tries to match the properties with arguments in the command line, with the format --<property>=<value>.
  • MemorySource: allows to define a series of properties in memory as use them as source of configuration.

There are some additional projects that provide property sources for some common configuration services, like DotNet.MultisourceConfiguration.Zookeeper, that provides a configuration source for ZooKeeper.

In addition to these property sources you can implement your own by providing implementations of the IStringConfigSource interface:

publicinterfaceIStringConfigSource{TimeSpanCacheExpiration{set;}boolTryGetString(stringproperty,outstringvalue);}

The configuration properties are overwritten by the given property sources in the order they are specified in the AddSources call. In the example above, the properties will be first read in the application settings file. Subsequently they will be overwritten with the properties found in environment variables (in case they are found). Finally, the properties will be overwritten with the values found in command-line arguments.

This provides a very convenient deployment behavior (specially for applications running in containers), in which applications take some default configuration from application settings, that is overwritten by the environment variables set in the machine (or container) and are finally overwritten with whatever has been provided in command line arguments.

It is also possible to perform several calls to AddSources. The given configuration sources are appended to the already existing list. This way, the new provided sources will overwrite configuration properties already set by configuration sources set in previous cllas to AddSources.

Property Types

The following types for configuration properties are available:

  • bool, bool?, bool[], List<bool>, IEnumerable<bool>
  • string, string[], List<string>, IEnumerable<string>
  • int, int?, int[], List<int>, IEnumerable<int>
  • long, long?, long[], List<long>, IEnumerable<long>
  • decimal, decimal?, decimal[], List<decimal>, IEnumerable<decimal>
  • float, float?, float[], List<float>, IEnumerable<float>
  • double, double?, double[], List<double>, IEnumerable<double>

In addition to these types, you can add your own type converters by providing implementations of the ITypeConverter interface to the AddTypeConverter<T>() method of ConfigurationBuilder. For convenience, the LambdaConverter is provided, that makes it easier to implement your own type converter:

configurationBuilder.AddTypeConverter(newLambdaConverter<MyType>(null/* Default value */, s =>MyType.Parse(s)/* Converter lambda */));

About

Configuration library with multiple sources for .NET

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - rubms/DotNet.MultiSourceConfiguration: Configuration library with multiple sources for .NET · GitHub
Skip to content

Repository files navigation

DotNet.MultiSourceConfiguration

NuGet VersionBuild Status

Configuration library with multiple sources for .NET.

Why DotNet.MultiSourceConfiguration

A very typical scenario in microservices (which typically run in containers) is to configure a service via a configuration file, and overwrite that configuration with whatever you can find in environment variables and command line. Used to Spring Boot's approach to configuration based in properties and property sources, I have struggled to find a simple library in .NET allowing to read configuration from different sources and overwrite it in a specified source order.

The Microsoft.Extensions.Configuration project follow a very similar approach but have some drawbacks:

  • It has a huge amount of dependencies.
  • At the moment of writing DotNet.MultiSourceConfiguration, the existing documentation was outdated and did not work with the last version of the library.

How to use it

The approach followed by DotNet.MultiSourceConfiguration is the population of configuration classes, that can subsequently be registered on an IOC container or made avaialable as a static property. The properties of the configuration class must be decorated with the Property attribute, indicating the name of the configuration property that must be mapped to the class property:

publicclassTestConfigurationDto{// By default properties are not required[Property("test.int.property")]publicint?IntProperty{get;set;}// The required condition of a property can be explicitly included.// If no value is provided InvalidOperationException is thrown.[Property("test.string.property",Required=true)]publicstringStringProperty{get;set;}// Properties can be marked as not required. The default value of the// given type converter will be applied (typically, null).[Property("test.long.property",Required=false)]publiclong?LongProperty{get;set;}// The "Default" property can be used to provide a default value in// case it is not provided via configuration.[Property("test.bool.property",Default="true")]publicbool?BoolProperty{get;set;}}

Configuration classes are populated via a configuration builder, which can be specified a series of sources:

classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

When adding sources to a configuration builder a priority can be specified. Higher priority numbers indicate higher priority (that is, the configuration provided by those sources will overwrite the configuration read from sources with lower priority). Given the same priority, sources will be evaluated in the order they are provided:

  • The properties provided by the first configuration source will be read.
  • The properties provided by the second confiration source will be read, overwriting whatever was read from previous sources, and so on.
classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();// Configuration is first read from configuration fileconfigurationBuilder.AddSources(1,newAppSettingsSource());// Configuration is then read form environment variables, overwriting the values read from app settings.configurationBuilder.AddSources(2,newEnvironmentVariableSource());// Configuration is finally read from command line arguments, overwriting the values read from app settings and environment variablesconfigurationBuilder.AddSources(3,newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

The configuration builder implements the IConfigurationBuilder interface. This way, the configuration builder is easier to mock in unit tests of classes that depend on it. A common pattern is to register the configuration builder in an IoC container and inject it in classes that need it.

The configuration builder has caching capabilities, allowing to re-use built configuration classes until a configurable cache expiration times out. When the cache expires and a configuration class is re-built, then the configuration is re-read from the sources. The cache expiration is configurable via the CacheExpiration property (by default the cache expiration is 0, i.e. no caching is done):

IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));configurationBuilder.CacheExpiration=TimeSpan.FromMinutes(2);

Properties

Properties in configuration objects that you want to populate with DotNet.MultiSourceConfiguration must be decorated with the Property annotation. This annotation receives the name of the configuration property that must be read from configuration in order to set the value to the configuration object property.

publicclassTestConfigurationDto{[Property("test.int.property")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("test.int.property","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.IntProperty);}

The Property annotation accepts the following attributes:

  • Required (bool): When set to true, the corresponding configuration property will be mandatory. An InvalidOperationException will be thrown if it is not possible to read the property value from at least one configuration source. Configuration properties are not required by default.
  • Default (string): A default value to set to the configuration property in case the it was not possible to read the property value from any configuration source.

Properties in the configuration object that are not decorated with the Property annotation will not be populated by default.

publicclassTestConfigurationDto{// By default only properties decorated with the Property annotation are populated[Property("test.int.property")]publicint?IntProperty{get;set;}// This property will be ignored, unless HandleNonDecoratedProperties is set to truepublicint?IgnoredProperty{get;set;}}

In case you want DotNet.MultiSourceConfiguration to also populate non-decorated configuration object properties you may set the HandleNonDecoratedProperties property of IConfigurationBuilder to true:

configurationBuilder.HandleNonDecoratedProperties=true;

In this case, the name of the configuration object property itself will be used as property name when retrieving the configuration from the configuration sources.

publicclassTestConfigurationDto{publicintNonDecoratedProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("NonDecoratedProperty","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);configurationBuilder.HandleNonDecoratedProperties=true;TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.NonDecoratedProperty);}

This behavior is useful when you are introducing DotNet.MultiSourceConfiguration in an already existing application that has a big number of configuration objects.

Property Prefixes

It is possible to indicate a prefix when building a configuration object in the IConfigurationBuilder.Build<T>(string propertiesPrefix) function. This will add the specified prefix to each one of the property names when trying to find the property value in the different configuration sources:

publicclassTestConfigurationDto{[Property("testProperty")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("myComponent1.testProperty","1");memoryConfigurationSource.Add("myComponent2.testProperty","2");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto1=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent1.");TestConfigurationDtoconfigurationDto2=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent2.");Assert.AreEqual(1,configurationDto1.IntProperty);Assert.AreEqual(2,configurationDto2.IntProperty);}

This is useful when you want to reuse the same configuration object in different contexts, each one having a different prefix.

Property Sources

The available, out-of-the-box, property sources are:

  • AppSettingsSource: looks for the properties in the .NET application settings file.
  • EnvironmentVariableSource: looks for the properties in the system environment variables.
  • CommandLineSource: tries to match the properties with arguments in the command line, with the format --<property>=<value>.
  • MemorySource: allows to define a series of properties in memory as use them as source of configuration.

There are some additional projects that provide property sources for some common configuration services, like DotNet.MultisourceConfiguration.Zookeeper, that provides a configuration source for ZooKeeper.

In addition to these property sources you can implement your own by providing implementations of the IStringConfigSource interface:

publicinterfaceIStringConfigSource{TimeSpanCacheExpiration{set;}boolTryGetString(stringproperty,outstringvalue);}

The configuration properties are overwritten by the given property sources in the order they are specified in the AddSources call. In the example above, the properties will be first read in the application settings file. Subsequently they will be overwritten with the properties found in environment variables (in case they are found). Finally, the properties will be overwritten with the values found in command-line arguments.

This provides a very convenient deployment behavior (specially for applications running in containers), in which applications take some default configuration from application settings, that is overwritten by the environment variables set in the machine (or container) and are finally overwritten with whatever has been provided in command line arguments.

It is also possible to perform several calls to AddSources. The given configuration sources are appended to the already existing list. This way, the new provided sources will overwrite configuration properties already set by configuration sources set in previous cllas to AddSources.

Property Types

The following types for configuration properties are available:

  • bool, bool?, bool[], List<bool>, IEnumerable<bool>
  • string, string[], List<string>, IEnumerable<string>
  • int, int?, int[], List<int>, IEnumerable<int>
  • long, long?, long[], List<long>, IEnumerable<long>
  • decimal, decimal?, decimal[], List<decimal>, IEnumerable<decimal>
  • float, float?, float[], List<float>, IEnumerable<float>
  • double, double?, double[], List<double>, IEnumerable<double>

In addition to these types, you can add your own type converters by providing implementations of the ITypeConverter interface to the AddTypeConverter<T>() method of ConfigurationBuilder. For convenience, the LambdaConverter is provided, that makes it easier to implement your own type converter:

configurationBuilder.AddTypeConverter(newLambdaConverter<MyType>(null/* Default value */, s =>MyType.Parse(s)/* Converter lambda */));

About

Configuration library with multiple sources for .NET

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - rubms/DotNet.MultiSourceConfiguration: Configuration library with multiple sources for .NET · GitHub
Skip to content

Repository files navigation

DotNet.MultiSourceConfiguration

NuGet VersionBuild Status

Configuration library with multiple sources for .NET.

Why DotNet.MultiSourceConfiguration

A very typical scenario in microservices (which typically run in containers) is to configure a service via a configuration file, and overwrite that configuration with whatever you can find in environment variables and command line. Used to Spring Boot's approach to configuration based in properties and property sources, I have struggled to find a simple library in .NET allowing to read configuration from different sources and overwrite it in a specified source order.

The Microsoft.Extensions.Configuration project follow a very similar approach but have some drawbacks:

  • It has a huge amount of dependencies.
  • At the moment of writing DotNet.MultiSourceConfiguration, the existing documentation was outdated and did not work with the last version of the library.

How to use it

The approach followed by DotNet.MultiSourceConfiguration is the population of configuration classes, that can subsequently be registered on an IOC container or made avaialable as a static property. The properties of the configuration class must be decorated with the Property attribute, indicating the name of the configuration property that must be mapped to the class property:

publicclassTestConfigurationDto{// By default properties are not required[Property("test.int.property")]publicint?IntProperty{get;set;}// The required condition of a property can be explicitly included.// If no value is provided InvalidOperationException is thrown.[Property("test.string.property",Required=true)]publicstringStringProperty{get;set;}// Properties can be marked as not required. The default value of the// given type converter will be applied (typically, null).[Property("test.long.property",Required=false)]publiclong?LongProperty{get;set;}// The "Default" property can be used to provide a default value in// case it is not provided via configuration.[Property("test.bool.property",Default="true")]publicbool?BoolProperty{get;set;}}

Configuration classes are populated via a configuration builder, which can be specified a series of sources:

classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

When adding sources to a configuration builder a priority can be specified. Higher priority numbers indicate higher priority (that is, the configuration provided by those sources will overwrite the configuration read from sources with lower priority). Given the same priority, sources will be evaluated in the order they are provided:

  • The properties provided by the first configuration source will be read.
  • The properties provided by the second confiration source will be read, overwriting whatever was read from previous sources, and so on.
classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();// Configuration is first read from configuration fileconfigurationBuilder.AddSources(1,newAppSettingsSource());// Configuration is then read form environment variables, overwriting the values read from app settings.configurationBuilder.AddSources(2,newEnvironmentVariableSource());// Configuration is finally read from command line arguments, overwriting the values read from app settings and environment variablesconfigurationBuilder.AddSources(3,newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

The configuration builder implements the IConfigurationBuilder interface. This way, the configuration builder is easier to mock in unit tests of classes that depend on it. A common pattern is to register the configuration builder in an IoC container and inject it in classes that need it.

The configuration builder has caching capabilities, allowing to re-use built configuration classes until a configurable cache expiration times out. When the cache expires and a configuration class is re-built, then the configuration is re-read from the sources. The cache expiration is configurable via the CacheExpiration property (by default the cache expiration is 0, i.e. no caching is done):

IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));configurationBuilder.CacheExpiration=TimeSpan.FromMinutes(2);

Properties

Properties in configuration objects that you want to populate with DotNet.MultiSourceConfiguration must be decorated with the Property annotation. This annotation receives the name of the configuration property that must be read from configuration in order to set the value to the configuration object property.

publicclassTestConfigurationDto{[Property("test.int.property")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("test.int.property","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.IntProperty);}

The Property annotation accepts the following attributes:

  • Required (bool): When set to true, the corresponding configuration property will be mandatory. An InvalidOperationException will be thrown if it is not possible to read the property value from at least one configuration source. Configuration properties are not required by default.
  • Default (string): A default value to set to the configuration property in case the it was not possible to read the property value from any configuration source.

Properties in the configuration object that are not decorated with the Property annotation will not be populated by default.

publicclassTestConfigurationDto{// By default only properties decorated with the Property annotation are populated[Property("test.int.property")]publicint?IntProperty{get;set;}// This property will be ignored, unless HandleNonDecoratedProperties is set to truepublicint?IgnoredProperty{get;set;}}

In case you want DotNet.MultiSourceConfiguration to also populate non-decorated configuration object properties you may set the HandleNonDecoratedProperties property of IConfigurationBuilder to true:

configurationBuilder.HandleNonDecoratedProperties=true;

In this case, the name of the configuration object property itself will be used as property name when retrieving the configuration from the configuration sources.

publicclassTestConfigurationDto{publicintNonDecoratedProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("NonDecoratedProperty","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);configurationBuilder.HandleNonDecoratedProperties=true;TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.NonDecoratedProperty);}

This behavior is useful when you are introducing DotNet.MultiSourceConfiguration in an already existing application that has a big number of configuration objects.

Property Prefixes

It is possible to indicate a prefix when building a configuration object in the IConfigurationBuilder.Build<T>(string propertiesPrefix) function. This will add the specified prefix to each one of the property names when trying to find the property value in the different configuration sources:

publicclassTestConfigurationDto{[Property("testProperty")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("myComponent1.testProperty","1");memoryConfigurationSource.Add("myComponent2.testProperty","2");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto1=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent1.");TestConfigurationDtoconfigurationDto2=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent2.");Assert.AreEqual(1,configurationDto1.IntProperty);Assert.AreEqual(2,configurationDto2.IntProperty);}

This is useful when you want to reuse the same configuration object in different contexts, each one having a different prefix.

Property Sources

The available, out-of-the-box, property sources are:

  • AppSettingsSource: looks for the properties in the .NET application settings file.
  • EnvironmentVariableSource: looks for the properties in the system environment variables.
  • CommandLineSource: tries to match the properties with arguments in the command line, with the format --<property>=<value>.
  • MemorySource: allows to define a series of properties in memory as use them as source of configuration.

There are some additional projects that provide property sources for some common configuration services, like DotNet.MultisourceConfiguration.Zookeeper, that provides a configuration source for ZooKeeper.

In addition to these property sources you can implement your own by providing implementations of the IStringConfigSource interface:

publicinterfaceIStringConfigSource{TimeSpanCacheExpiration{set;}boolTryGetString(stringproperty,outstringvalue);}

The configuration properties are overwritten by the given property sources in the order they are specified in the AddSources call. In the example above, the properties will be first read in the application settings file. Subsequently they will be overwritten with the properties found in environment variables (in case they are found). Finally, the properties will be overwritten with the values found in command-line arguments.

This provides a very convenient deployment behavior (specially for applications running in containers), in which applications take some default configuration from application settings, that is overwritten by the environment variables set in the machine (or container) and are finally overwritten with whatever has been provided in command line arguments.

It is also possible to perform several calls to AddSources. The given configuration sources are appended to the already existing list. This way, the new provided sources will overwrite configuration properties already set by configuration sources set in previous cllas to AddSources.

Property Types

The following types for configuration properties are available:

  • bool, bool?, bool[], List<bool>, IEnumerable<bool>
  • string, string[], List<string>, IEnumerable<string>
  • int, int?, int[], List<int>, IEnumerable<int>
  • long, long?, long[], List<long>, IEnumerable<long>
  • decimal, decimal?, decimal[], List<decimal>, IEnumerable<decimal>
  • float, float?, float[], List<float>, IEnumerable<float>
  • double, double?, double[], List<double>, IEnumerable<double>

In addition to these types, you can add your own type converters by providing implementations of the ITypeConverter interface to the AddTypeConverter<T>() method of ConfigurationBuilder. For convenience, the LambdaConverter is provided, that makes it easier to implement your own type converter:

configurationBuilder.AddTypeConverter(newLambdaConverter<MyType>(null/* Default value */, s =>MyType.Parse(s)/* Converter lambda */));

About

Configuration library with multiple sources for .NET

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - rubms/DotNet.MultiSourceConfiguration: Configuration library with multiple sources for .NET · GitHub
Skip to content

Repository files navigation

DotNet.MultiSourceConfiguration

NuGet VersionBuild Status

Configuration library with multiple sources for .NET.

Why DotNet.MultiSourceConfiguration

A very typical scenario in microservices (which typically run in containers) is to configure a service via a configuration file, and overwrite that configuration with whatever you can find in environment variables and command line. Used to Spring Boot's approach to configuration based in properties and property sources, I have struggled to find a simple library in .NET allowing to read configuration from different sources and overwrite it in a specified source order.

The Microsoft.Extensions.Configuration project follow a very similar approach but have some drawbacks:

  • It has a huge amount of dependencies.
  • At the moment of writing DotNet.MultiSourceConfiguration, the existing documentation was outdated and did not work with the last version of the library.

How to use it

The approach followed by DotNet.MultiSourceConfiguration is the population of configuration classes, that can subsequently be registered on an IOC container or made avaialable as a static property. The properties of the configuration class must be decorated with the Property attribute, indicating the name of the configuration property that must be mapped to the class property:

publicclassTestConfigurationDto{// By default properties are not required[Property("test.int.property")]publicint?IntProperty{get;set;}// The required condition of a property can be explicitly included.// If no value is provided InvalidOperationException is thrown.[Property("test.string.property",Required=true)]publicstringStringProperty{get;set;}// Properties can be marked as not required. The default value of the// given type converter will be applied (typically, null).[Property("test.long.property",Required=false)]publiclong?LongProperty{get;set;}// The "Default" property can be used to provide a default value in// case it is not provided via configuration.[Property("test.bool.property",Default="true")]publicbool?BoolProperty{get;set;}}

Configuration classes are populated via a configuration builder, which can be specified a series of sources:

classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

When adding sources to a configuration builder a priority can be specified. Higher priority numbers indicate higher priority (that is, the configuration provided by those sources will overwrite the configuration read from sources with lower priority). Given the same priority, sources will be evaluated in the order they are provided:

  • The properties provided by the first configuration source will be read.
  • The properties provided by the second confiration source will be read, overwriting whatever was read from previous sources, and so on.
classProgram{staticvoidMain(string[]args){IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();// Configuration is first read from configuration fileconfigurationBuilder.AddSources(1,newAppSettingsSource());// Configuration is then read form environment variables, overwriting the values read from app settings.configurationBuilder.AddSources(2,newEnvironmentVariableSource());// Configuration is finally read from command line arguments, overwriting the values read from app settings and environment variablesconfigurationBuilder.AddSources(3,newCommandLineSource(args));TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();
...}}

The configuration builder implements the IConfigurationBuilder interface. This way, the configuration builder is easier to mock in unit tests of classes that depend on it. A common pattern is to register the configuration builder in an IoC container and inject it in classes that need it.

The configuration builder has caching capabilities, allowing to re-use built configuration classes until a configurable cache expiration times out. When the cache expires and a configuration class is re-built, then the configuration is re-read from the sources. The cache expiration is configurable via the CacheExpiration property (by default the cache expiration is 0, i.e. no caching is done):

IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(newAppSettingsSource(),newEnvironmentVariableSource(),newCommandLineSource(args));configurationBuilder.CacheExpiration=TimeSpan.FromMinutes(2);

Properties

Properties in configuration objects that you want to populate with DotNet.MultiSourceConfiguration must be decorated with the Property annotation. This annotation receives the name of the configuration property that must be read from configuration in order to set the value to the configuration object property.

publicclassTestConfigurationDto{[Property("test.int.property")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("test.int.property","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.IntProperty);}

The Property annotation accepts the following attributes:

  • Required (bool): When set to true, the corresponding configuration property will be mandatory. An InvalidOperationException will be thrown if it is not possible to read the property value from at least one configuration source. Configuration properties are not required by default.
  • Default (string): A default value to set to the configuration property in case the it was not possible to read the property value from any configuration source.

Properties in the configuration object that are not decorated with the Property annotation will not be populated by default.

publicclassTestConfigurationDto{// By default only properties decorated with the Property annotation are populated[Property("test.int.property")]publicint?IntProperty{get;set;}// This property will be ignored, unless HandleNonDecoratedProperties is set to truepublicint?IgnoredProperty{get;set;}}

In case you want DotNet.MultiSourceConfiguration to also populate non-decorated configuration object properties you may set the HandleNonDecoratedProperties property of IConfigurationBuilder to true:

configurationBuilder.HandleNonDecoratedProperties=true;

In this case, the name of the configuration object property itself will be used as property name when retrieving the configuration from the configuration sources.

publicclassTestConfigurationDto{publicintNonDecoratedProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("NonDecoratedProperty","123");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);configurationBuilder.HandleNonDecoratedProperties=true;TestConfigurationDtoconfigurationDto=configurationBuilder.Build<TestConfigurationDto>();Assert.AreEqual(123,configurationDto.NonDecoratedProperty);}

This behavior is useful when you are introducing DotNet.MultiSourceConfiguration in an already existing application that has a big number of configuration objects.

Property Prefixes

It is possible to indicate a prefix when building a configuration object in the IConfigurationBuilder.Build<T>(string propertiesPrefix) function. This will add the specified prefix to each one of the property names when trying to find the property value in the different configuration sources:

publicclassTestConfigurationDto{[Property("testProperty")]publicintIntProperty{get;set;}}[Test]publicvoidTest(){varmemoryConfigurationSource=newMemorySource();memoryConfigurationSource.Add("myComponent1.testProperty","1");memoryConfigurationSource.Add("myComponent2.testProperty","2");IConfigurationBuilderconfigurationBuilder=newConfigurationBuilder();configurationBuilder.AddSources(memoryConfigurationSource);TestConfigurationDtoconfigurationDto1=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent1.");TestConfigurationDtoconfigurationDto2=configurationBuilder.Build<TestConfigurationDto>(propertiesPrefix:"myComponent2.");Assert.AreEqual(1,configurationDto1.IntProperty);Assert.AreEqual(2,configurationDto2.IntProperty);}

This is useful when you want to reuse the same configuration object in different contexts, each one having a different prefix.

Property Sources

The available, out-of-the-box, property sources are:

  • AppSettingsSource: looks for the properties in the .NET application settings file.
  • EnvironmentVariableSource: looks for the properties in the system environment variables.
  • CommandLineSource: tries to match the properties with arguments in the command line, with the format --<property>=<value>.
  • MemorySource: allows to define a series of properties in memory as use them as source of configuration.

There are some additional projects that provide property sources for some common configuration services, like DotNet.MultisourceConfiguration.Zookeeper, that provides a configuration source for ZooKeeper.

In addition to these property sources you can implement your own by providing implementations of the IStringConfigSource interface:

publicinterfaceIStringConfigSource{TimeSpanCacheExpiration{set;}boolTryGetString(stringproperty,outstringvalue);}

The configuration properties are overwritten by the given property sources in the order they are specified in the AddSources call. In the example above, the properties will be first read in the application settings file. Subsequently they will be overwritten with the properties found in environment variables (in case they are found). Finally, the properties will be overwritten with the values found in command-line arguments.

This provides a very convenient deployment behavior (specially for applications running in containers), in which applications take some default configuration from application settings, that is overwritten by the environment variables set in the machine (or container) and are finally overwritten with whatever has been provided in command line arguments.

It is also possible to perform several calls to AddSources. The given configuration sources are appended to the already existing list. This way, the new provided sources will overwrite configuration properties already set by configuration sources set in previous cllas to AddSources.

Property Types

The following types for configuration properties are available:

  • bool, bool?, bool[], List<bool>, IEnumerable<bool>
  • string, string[], List<string>, IEnumerable<string>
  • int, int?, int[], List<int>, IEnumerable<int>
  • long, long?, long[], List<long>, IEnumerable<long>
  • decimal, decimal?, decimal[], List<decimal>, IEnumerable<decimal>
  • float, float?, float[], List<float>, IEnumerable<float>
  • double, double?, double[], List<double>, IEnumerable<double>

In addition to these types, you can add your own type converters by providing implementations of the ITypeConverter interface to the AddTypeConverter<T>() method of ConfigurationBuilder. For convenience, the LambdaConverter is provided, that makes it easier to implement your own type converter:

configurationBuilder.AddTypeConverter(newLambdaConverter<MyType>(null/* Default value */, s =>MyType.Parse(s)/* Converter lambda */));

About

Configuration library with multiple sources for .NET

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages