{"id":23425971,"url":"https://github.com/davidwhitney/system.configuration.abstractions","last_synced_at":"2025-09-09T14:19:11.643Z","repository":{"id":11671526,"uuid":"14180924","full_name":"davidwhitney/System.Configuration.Abstractions","owner":"davidwhitney","description":"Injectable, mockable, extensible, configuration for .NET","archived":false,"fork":false,"pushed_at":"2020-05-03T14:03:38.000Z","size":2068,"stargazers_count":42,"open_issues_count":3,"forks_count":7,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-10T21:40:21.969Z","etag":null,"topics":["appsettings","c-sharp","configuration","converter","wrapper"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidwhitney.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-11-06T18:01:49.000Z","updated_at":"2024-03-01T18:14:06.000Z","dependencies_parsed_at":"2022-07-14T02:50:34.905Z","dependency_job_id":null,"html_url":"https://github.com/davidwhitney/System.Configuration.Abstractions","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwhitney%2FSystem.Configuration.Abstractions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwhitney%2FSystem.Configuration.Abstractions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwhitney%2FSystem.Configuration.Abstractions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwhitney%2FSystem.Configuration.Abstractions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidwhitney","download_url":"https://codeload.github.com/davidwhitney/System.Configuration.Abstractions/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248610364,"owners_count":21132920,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appsettings","c-sharp","configuration","converter","wrapper"],"created_at":"2024-12-23T05:15:32.204Z","updated_at":"2025-04-12T17:50:34.675Z","avatar_url":"https://github.com/davidwhitney.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"System.Configuration.Abstractions [![Build status](https://ci.appveyor.com/api/projects/status/ngl0cknxt74bfnve)](https://ci.appveyor.com/project/DavidWhitney/system-configuration-abstractions)\n====================\n\n* Introduction\n* Installation\n* Getting started\n* Features\n\t* Generic typed helper methods for retrieving typed config values\n\t* Configuration Interceptors\n\t* Configuration Substitution Interceptor\n* Contributing\n* Credits\n\n---\n\nIn most projects, you're going to have some configuration. In .NET projects, it'll probably start in your app.config or web.config file.\n\nHowever, if you love TDD, you'll likely have notice that all of the built in configuration classes are horribly un-testable. They all revolve around static references to System.Configuration.ConfigurationManager, and don't really have any interfaces, so in every project, you end up wrapping them into something like \"IAppSettingsWrapper\", in order to write tests.\n\nAfter writing these wrappers several thousand times, and being inspired by the excellent \"System.IO.Abstractions\" package, we've put together a standardised set of wrappers around these core framework classes.\n\nIf you want\n\n* to **mock/stub/whatever** out your App/Web.config files\n* to assert that the values from configuration are *really* configuring your application\n* to **add custom hooks** around loading configuration values\n* **stronger typing**\n\nThe this is for you.\n\n# Installation\n\n* From source: https://github.com/davidwhitney/System.Configuration.Abstractions\n* By hand: https://www.nuget.org/packages/System.Configuration.Abstractions\n\nVia NuGet:\n\n\t\tPM\u003e Install-Package System.Configuration.Abstractions\n\n# Getting started\n\nThe simplest use case is to bind up `IConfigurationManager` to `System.Configuration.Abstractions.ConfigurationManager` in your DI container.\nAlternatively, you can use `System.Configuration.Abstractions.ConfigurationManager.Instance` - a property that'll new up a new instance of IConfigurationManager each time it's accessed.\n\nIf you want to directly switch out calls to `System.Configuration.ConfigurationManager` in-place, to take advantage of the strongly typed extensions and `IConfigurationInterceptors` you can replace calls to `System.Configuration.ConfigurationManager` with calls to `System.Configuration.Abstractions.ConfigurationManager.Instance` in-line, and your code should function identically.\n\nLastly, you can just new up an instance of `System.Configuration.Abstractions.ConfigurationManager` anywhere, using its default constructor, and everything'll be just fine.\n\nExamples:\n\n```csharp\n    // Usages\n\n    // You can use the singleton\n    var valString = ConfigurationManager.Instance.AppSettings[\"stringKey\"];\n    var valInt = ConfigurationManager.Instance.AppSettings.AppSetting\u003cint\u003e(\"intKey\");\n\n    // You can new up an instance\n    var configMgr = new ConfigurationManager();\n    var valString2 = configMgr.AppSettings[\"stringKey\"];\n    var valInt2 = configMgr.AppSettings.AppSetting\u003cint\u003e(\"intKey\");\n\n    // You can new up an instance with configuration values\n    var configMgr3 = new ConfigurationManager(new NameValueCollection {{\"stringKey\", \"hello\"}, {\"intKey\", \"123\"}});\n    var valString3 = configMgr3.AppSettings[\"stringKey\"];\n    var valInt3 = configMgr3.AppSettings.AppSetting\u003cint\u003e(\"intKey\");\n\n    // You can just switch out calls in place for backwards compatible behaviour\n    var old = System.Configuration.ConfigurationManager.AppSettings[\"stringKey\"];\n    var @new = ConfigurationManager.Instance.AppSettings[\"stringKey\"];\n\n    // You can wire up to your container\n    ninjectContainer.Bind\u003cIConfigurationManager\u003e().ToMethod(()=\u003e return new ConfigurationManager());\n```\n\n# Features\n\n## Generic typed helper methods for retrieving typed config values\n\nThe `IAppSettingsExtended` interface, which our `AppSettingsExtended` class implements, contains four new methods:\n\n* `string AppSetting(string key, Func\u003cstring\u003e whenKeyNotFoundInsteadOfThrowingDefaultException = null);`\n* `T AppSetting\u003cT\u003e(string key, Func\u003cT\u003e whenKeyNotFoundInsteadOfThrowingDefaultException = null);`\n* `T AppSettingConvert\u003cT\u003e(string key, Func\u003cT\u003e whenConversionFailsInsteadOfThrowingDefaultException = null);`\n* `T AppSettingSilent\u003cT\u003e(string key, Func\u003cT\u003e insteadOfThrowingDefaultException = null);`\n\nThese strongly typed \"AppSetting\" helpers, will convert any primitive types that `Convert.ChangeType` supports. The most obvious use case being int / bool / float / int? / bool? from their string representations - keeping alot of noisy conversions out of your code. You can also provide an optional Func\u003cT\u003e which will get invoked if the key you're requesting is not found - otherwise, we'll throw an exception.\n\n\nThe following usage examples illustrate how to use these helpers:\n```csharp\n    // Before *******************************\n\n    var settingThatIsAnInteger = System.Configuration.ConfigurationManager.AppSettings[\"key\"];\n    int someInt;\n    if (Int32.TryParse(settingThatIsAnInteger, out someInt))\n    {\n        someInt = 123; // Default\n    }\n\n    var settingThatIsABool = System.Configuration.ConfigurationManager.AppSettings[\"otherKey\"];\n    bool someBool;\n    if (bool.TryParse(settingThatIsAnInteger, out someBool))\n    {\n        someBool = true; // Default\n    }\n\n\tbool? someBool;\n\ttry\n\t{\n\t\tvar settingThatIsABool = System.Configuration.ConfigurationManager.AppSettings[\"otherKey\"];\n\t\tif (bool.TryParse(settingThatIsAnInteger, out someBool))\n\t\t{\n\t\t\tsomeBool = true; // Default\n\t\t}\n\t}\n\tcatch\n\t{\n\t\tsomeBool = true;\n\t}\n\n    // After ********************************\n\n    var withADefault = ConfigurationManager.Instance.AppSettings.AppSetting(\"key\", () =\u003e 123);\n    var withoutADefault = ConfigurationManager.Instance.AppSettings.AppSetting\u003cint\u003e(\"key\");\n\n    var worksWithAllPrimatives = ConfigurationManager.Instance.AppSettings.AppSetting\u003cbool\u003e(\"otherKey\");\n    var worksWithNullables = ConfigurationManager.Instance.AppSettings.AppSetting\u003cbool?\u003e(\"otherKey\");\n\n    var customNotFoundHandler = ConfigurationManager.Instance.AppSettings.AppSetting\u003cbool?\u003e(\"otherKey\", () =\u003e\n    {\n        throw new MyCustomMissingKeyException();\n    });\n\n    var customNotFoundHandler = ConfigurationManager.Instance.AppSettings.AppSetting\u003cbool?\u003e(\"otherKey\", () =\u003e\n    {\n        throw new MyCustomMissingKeyException();\n    },\n\t() =\u003e\n    {\n        throw new MyCustomConversionException();\n    });\n\n    var customNotFoundHandler = configurationManagerExtended.AppSettingConvert\u003cbool?\u003e(\"otherKey\", () =\u003e\n    {\n        throw new MyCustomConversionException();\n    });\n\n\tvar defaultValue = true;\n\tvar silentHandler = configurationManagerExtended.AppSettingSilent\u003cbool\u003e(\"otherKey\", () =\u003e\n    {\n\t\t// Log Warning\n\t\t// ...\n\n\t\t// Return Default\n        return defaultValue;\n    });\n```\n\n## IConfigurationInterceptors\n\n`IConfigurationInterceptor`'s are hooks that, if registered, allow you to intercept and manipulate the values retrieved from configuration.\n`IConnectionStringInterceptor`'s are hooks that allow you to manipulate connection strings during retrieval in a similar manner.\n\nTo wire up an `IConfigurationInterceptor` or `IConnectionStringInterceptor`, first, implement one, then call the static method `ConfigurationManager.RegisterInterceptors(interceptor);`\nYour interceptors are singletons and should be thread safe as the same instance could be called across multiple threads concurrently.\n\nExample:\n\n```csharp\n\tConfigurationManager.RegisterInterceptors(new ConfigurationSubstitutionInterceptor());\n\tvar result = ConfigurationManager.Instance.AppSettings.AppSetting\u003cstring\u003e(\"key\"); // Interceptor executes\n\tvar result2 = ConfigurationManager.Instance.AppSettings[\"key\"]; // Interceptor executes\n```\n\nInterceptors fire for both the AppSetting helper, and the standard NameValueCollection methods and indexers. If you want to by-pass interception, access the \"Raw\" property for the original collection. *This is a change in behaviour in V2*.\n\n### Why would I want interceptors?\n\nAn obvious example would be the presence of an appSetting looking like this:\n```xml\n    \u003cadd key=\"my-key\" value=\"{machineName}-something\" /\u003e\n```\nYou could easily add an interceptor to detect and fill in `{machineName}` from an environmental variable, keeping your configuration free of painful transformations.\nThere are several other useful scenarios (auditing and logging, substitution, multi-tenancy) that interceptors could be useful in.\n\n## Included Interceptors\n\n### ConfigurationSubstitutionInterceptor\n\nThe `ConfigurationSubstitutionInterceptor` is bundled with the package, firstly as an example, but also as a useful configuration interceptor.\nIt supports embedding any appsetting into any other.  Given:\n```xml\n    \u003cadd key=\"key1\" value=\"valueOfOne\" /\u003e\n    \u003cadd key=\"key2\" value=\"{key1}-valueOfTwo\" /\u003e\n```\nWith this interceptor registered, this is true:\n```csharp\n\tvar result = ConfigurationManager.AppSetting\u003cstring\u003e(\"key2\");\n\tConsole.WriteLine(result); // writes: valueOfOne-valueOfTwo\n```\nThis interceptor will help you simplify transformed web or app config files that rely on similar / repetitive token replacements, by allowing you to override just one value, and have it nested across the rest of your configuration using the interceptor.\n\n# ITypeConverters\n\nWired up just like `IConfigurationInterceptors` - TypeConverters let you specify custom type conversion logic.\nWe include one TypeConverter by default - that converters to `Uri`. To implement your own type converter, you need to implement the following interface:\n\n```csharp\npublic interface IConvertType\n{\n\tType TargetType { get; }\n\tobject Convert(string configurationValue);\n}\n```\nand register your converter by using\n\n```csharp\nvar converter = new UserConverterExample();\nConfigurationManager.RegisterTypeConverters(converter);\n```\n\nYour type converter will then be invoked whenever you request a mapping to the type that your converter supports.\n\n# Contributing\n\nSend a pull request with a passing test for any bugs or interesting extension ideas.\n\n# Credits\n\nDavid Whitney\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidwhitney%2Fsystem.configuration.abstractions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidwhitney%2Fsystem.configuration.abstractions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidwhitney%2Fsystem.configuration.abstractions/lists"}