Repository: aspnet/Configuration Branch: master Commit: f64994e06556 Files: 179 Total size: 671.0 KB Directory structure: gitextract_p1se3q03/ ├── .appveyor.yml ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .vsts-pipelines/ │ └── builds/ │ ├── ci-internal.yml │ └── ci-public.yml ├── CONTRIBUTING.md ├── Configuration.sln ├── Directory.Build.props ├── Directory.Build.targets ├── LICENSE.txt ├── NuGet.config ├── NuGetPackageVerifier.json ├── README.md ├── build/ │ ├── Key.snk │ ├── dependencies.props │ ├── repo.props │ └── sources.props ├── build.cmd ├── build.sh ├── korebuild-lock.txt ├── korebuild.json ├── run.cmd ├── run.ps1 ├── run.sh ├── samples/ │ └── KeyVaultSample/ │ ├── EnvironmentSecretManager.cs │ ├── KeyVaultSample.csproj │ ├── Program.cs │ └── settings.json ├── src/ │ ├── Config/ │ │ ├── ChainedBuilderExtensions.cs │ │ ├── ChainedConfigurationProvider.cs │ │ ├── ChainedConfigurationSource.cs │ │ ├── Config.csproj │ │ ├── ConfigurationBuilder.cs │ │ ├── ConfigurationKeyComparer.cs │ │ ├── ConfigurationProvider.cs │ │ ├── ConfigurationReloadToken.cs │ │ ├── ConfigurationRoot.cs │ │ ├── ConfigurationSection.cs │ │ ├── MemoryConfigurationBuilderExtensions.cs │ │ ├── MemoryConfigurationProvider.cs │ │ ├── MemoryConfigurationSource.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ └── Resources.Designer.cs │ │ ├── Resources.resx │ │ └── baseline.netcore.json │ ├── Config.Abstractions/ │ │ ├── Config.Abstractions.csproj │ │ ├── ConfigurationExtensions.cs │ │ ├── ConfigurationPath.cs │ │ ├── IConfiguration.cs │ │ ├── IConfigurationBuilder.cs │ │ ├── IConfigurationProvider.cs │ │ ├── IConfigurationRoot.cs │ │ ├── IConfigurationSection.cs │ │ ├── IConfigurationSource.cs │ │ └── baseline.netcore.json │ ├── Config.AzureKeyVault/ │ │ ├── AzureKeyVaultConfigurationExtensions.cs │ │ ├── AzureKeyVaultConfigurationProvider.cs │ │ ├── AzureKeyVaultConfigurationSource.cs │ │ ├── Config.AzureKeyVault.csproj │ │ ├── DefaultKeyVaultSecretManager.cs │ │ ├── IKeyVaultClient.cs │ │ ├── IKeyVaultSecretManager.cs │ │ ├── KeyVaultClientWrapper.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ └── baseline.netcore.json │ ├── Config.Binder/ │ │ ├── BinderOptions.cs │ │ ├── Config.Binder.csproj │ │ ├── ConfigurationBinder.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ └── Resources.Designer.cs │ │ ├── Resources.resx │ │ └── baseline.netcore.json │ ├── Config.CommandLine/ │ │ ├── CommandLineConfigurationExtensions.cs │ │ ├── CommandLineConfigurationProvider.cs │ │ ├── CommandLineConfigurationSource.cs │ │ ├── Config.CommandLine.csproj │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ └── Resources.Designer.cs │ │ ├── Resources.resx │ │ └── baseline.netcore.json │ ├── Config.EnvironmentVariables/ │ │ ├── Config.EnvironmentVariables.csproj │ │ ├── EnvironmentVariablesConfigurationProvider.cs │ │ ├── EnvironmentVariablesConfigurationSource.cs │ │ ├── EnvironmentVariablesExtensions.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ └── baseline.netcore.json │ ├── Config.FileExtensions/ │ │ ├── Config.FileExtensions.csproj │ │ ├── FileConfigurationExtensions.cs │ │ ├── FileConfigurationProvider.cs │ │ ├── FileConfigurationSource.cs │ │ ├── FileLoadExceptionContext.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ └── Resources.Designer.cs │ │ ├── Resources.resx │ │ └── baseline.netcore.json │ ├── Config.Ini/ │ │ ├── Config.Ini.csproj │ │ ├── IniConfigurationExtensions.cs │ │ ├── IniConfigurationProvider.cs │ │ ├── IniConfigurationSource.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ └── Resources.Designer.cs │ │ ├── Resources.resx │ │ └── baseline.netcore.json │ ├── Config.Json/ │ │ ├── Config.Json.csproj │ │ ├── JsonConfigurationExtensions.cs │ │ ├── JsonConfigurationFileParser.cs │ │ ├── JsonConfigurationProvider.cs │ │ ├── JsonConfigurationSource.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ └── Resources.Designer.cs │ │ ├── Resources.resx │ │ └── baseline.netcore.json │ ├── Config.KeyPerFile/ │ │ ├── Config.KeyPerFile.csproj │ │ ├── KeyPerFileConfigurationBuilderExtensions.cs │ │ ├── KeyPerFileConfigurationProvider.cs │ │ ├── KeyPerFileConfigurationSource.cs │ │ └── README.md │ ├── Config.UserSecrets/ │ │ ├── Config.UserSecrets.csproj │ │ ├── PathHelper.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ └── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── UserSecretsConfigurationExtensions.cs │ │ ├── UserSecretsIdAttribute.cs │ │ ├── baseline.netcore.json │ │ └── build/ │ │ └── netstandard2.0/ │ │ ├── Microsoft.Extensions.Configuration.UserSecrets.props │ │ └── Microsoft.Extensions.Configuration.UserSecrets.targets │ ├── Config.Xml/ │ │ ├── Config.Xml.csproj │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ └── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── XmlConfigurationExtensions.cs │ │ ├── XmlConfigurationProvider.cs │ │ ├── XmlConfigurationSource.cs │ │ ├── XmlDocumentDecryptor.cs │ │ └── baseline.netcore.json │ └── Directory.Build.props ├── test/ │ ├── Config.AzureKeyVault.Test/ │ │ ├── AzureKeyVaultConfigurationTest.cs │ │ └── Config.AzureKeyVault.Test.csproj │ ├── Config.Binder.Test/ │ │ ├── Config.Binder.Test.csproj │ │ ├── ConfigurationBinderTests.cs │ │ └── ConfigurationCollectionBindingTests.cs │ ├── Config.CommandLine.Test/ │ │ ├── CommandLineTest.cs │ │ └── Config.CommandLine.Test.csproj │ ├── Config.EnvironmentVariables.Test/ │ │ ├── Config.EnvironmentVariables.Test.csproj │ │ └── EnvironmentVariablesTest.cs │ ├── Config.FileExtensions.Test/ │ │ ├── Config.FileExtensions.Test.csproj │ │ └── FileConfigurationBuilderExtensionsTest.cs │ ├── Config.FunctionalTests/ │ │ ├── ArrayTests.cs │ │ ├── Config.FunctionalTests.csproj │ │ ├── ConfigurationTests.cs │ │ ├── DisposableFileSystem.cs │ │ └── test.xml │ ├── Config.Ini.Test/ │ │ ├── Config.Ini.Test.csproj │ │ ├── IniConfigurationExtensionsTest.cs │ │ └── IniConfigurationTest.cs │ ├── Config.Json.Test/ │ │ ├── ArrayTest.cs │ │ ├── Config.Json.Test.csproj │ │ ├── JsonConfigurationExtensionsTest.cs │ │ └── JsonConfigurationTest.cs │ ├── Config.KeyPerFile.Test/ │ │ ├── Config.KeyPerFile.Test.csproj │ │ └── KeyPerFileTests.cs │ ├── Config.Test/ │ │ ├── Config.Test.csproj │ │ ├── ConfigurationPathComparerTest.cs │ │ ├── ConfigurationPathTest.cs │ │ └── ConfigurationTest.cs │ ├── Config.Test.Common/ │ │ ├── Config.Test.Common.csproj │ │ ├── ConfigurationProviderExtensions.cs │ │ └── TestStreamHelpers.cs │ ├── Config.UserSecrets.Test/ │ │ ├── Config.UserSecrets.Test.csproj │ │ ├── ConfigurationExtensionTest.cs │ │ ├── MsBuildTargetTest.cs │ │ └── PathHelperTest.cs │ ├── Config.Xml.Test/ │ │ ├── Config.Xml.Test.csproj │ │ ├── XmlConfigurationExtensionsTest.cs │ │ └── XmlConfigurationTest.cs │ └── Directory.Build.props └── version.props ================================================ FILE CONTENTS ================================================ ================================================ FILE: .appveyor.yml ================================================ init: - git config --global core.autocrlf true branches: only: - master - /^release\/.*$/ - /^(.*\/)?ci-.*$/ build_script: - ps: .\run.ps1 default-build clone_depth: 1 environment: global: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_CLI_TELEMETRY_OPTOUT: 1 test: 'off' deploy: 'off' os: Visual Studio 2017 ================================================ FILE: .gitattributes ================================================ *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain *.jpg binary *.png binary *.gif binary *.cs text=auto diff=csharp *.vb text=auto *.resx text=auto *.c text=auto *.cpp text=auto *.cxx text=auto *.h text=auto *.hxx text=auto *.py text=auto *.rb text=auto *.java text=auto *.html text=auto *.htm text=auto *.css text=auto *.scss text=auto *.sass text=auto *.less text=auto *.js text=auto *.lisp text=auto *.clj text=auto *.sql text=auto *.php text=auto *.lua text=auto *.m text=auto *.asm text=auto *.erl text=auto *.fs text=auto *.fsx text=auto *.hs text=auto *.csproj text=auto *.vbproj text=auto *.fsproj text=auto *.dbproj text=auto *.sln text=auto eol=crlf *.sh eol=lf ================================================ FILE: .gitignore ================================================ [Oo]bj/ [Bb]in/ TestResults/ .nuget/ _ReSharper.*/ packages/ artifacts/ PublishProfiles/ *.user *.suo *.cache *.docstates _ReSharper.* nuget.exe *net45.csproj *net451.csproj *k10.csproj *.psess *.vsp *.pidb *.userprefs *DS_Store *.ncrunchsolution *.*sdf *.ipch *.sln.ide project.lock.json .vs .vscode/ .build/ .testPublish/ *.nuget.props *.nuget.targets global.json ================================================ FILE: .travis.yml ================================================ language: csharp sudo: false dist: trusty env: global: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - DOTNET_CLI_TELEMETRY_OPTOUT: 1 mono: none os: - linux - osx osx_image: xcode8.2 addons: apt: packages: - libunwind8 branches: only: - master - /^release\/.*$/ - /^(.*\/)?ci-.*$/ before_install: - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; fi script: - ./build.sh ================================================ FILE: .vsts-pipelines/builds/ci-internal.yml ================================================ trigger: - master - release/* resources: repositories: - repository: buildtools type: git name: aspnet-BuildTools ref: refs/heads/master phases: - template: .vsts-pipelines/templates/project-ci.yml@buildtools ================================================ FILE: .vsts-pipelines/builds/ci-public.yml ================================================ trigger: - master - release/* # See https://github.com/aspnet/BuildTools resources: repositories: - repository: buildtools type: github endpoint: DotNet-Bot GitHub Connection name: aspnet/BuildTools ref: refs/heads/master phases: - template: .vsts-pipelines/templates/project-ci.yml@buildtools ================================================ FILE: CONTRIBUTING.md ================================================ Contributing ====== Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo. ================================================ FILE: Configuration.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26424.2 MinimumVisualStudioVersion = 15.0.26730.03 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1}" ProjectSection(SolutionItems) = preProject src\Directory.Build.props = src\Directory.Build.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B54371FF-B920-46C8-8D55-6B19DBB43EBF}" ProjectSection(SolutionItems) = preProject test\Directory.Build.props = test\Directory.Build.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Json", "src\Config.Json\Config.Json.csproj", "{4C4CD1BC-4411-4AFD-9D04-147053F0E259}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Xml", "src\Config.Xml\Config.Xml.csproj", "{1BEC97C1-56B9-4B2B-A95A-C0DF72F1E96A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Json.Test", "test\Config.Json.Test\Config.Json.Test.csproj", "{AE8F8C20-9ED9-4A16-9565-27DF77683789}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Xml.Test", "test\Config.Xml.Test\Config.Xml.Test.csproj", "{0786C785-944A-4423-96A6-4E7BFDB4A1B0}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Test", "test\Config.Test\Config.Test.csproj", "{8777C77E-CA2A-42C1-90CD-2EA9CBF28937}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration", "src\Config\Config.csproj", "{62BD48B5-BB0C-4C2C-9C4B-04CF75CDCCF1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.FunctionalTests", "test\Config.FunctionalTests\Config.FunctionalTests.csproj", "{EAC77F15-F12E-496B-9184-1B1DA89BFFE9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Abstractions", "src\Config.Abstractions\Config.Abstractions.csproj", "{3F1CB08E-9FBD-4CAE-A78A-4AC43F24FC49}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Test.Common", "test\Config.Test.Common\Config.Test.Common.csproj", "{29C120E5-F682-4BFB-826B-040A594802CA}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.CommandLine", "src\Config.CommandLine\Config.CommandLine.csproj", "{D4B7CF9B-4229-44DC-800F-CC39150CEAB2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Ini", "src\Config.Ini\Config.Ini.csproj", "{C555C5D5-BF4A-451E-AB43-EBF4DE885EC7}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.EnvironmentVariables", "src\Config.EnvironmentVariables\Config.EnvironmentVariables.csproj", "{A6A2C665-E5A4-4FD3-AD0C-E33E6CFFCB88}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.CommandLine.Test", "test\Config.CommandLine.Test\Config.CommandLine.Test.csproj", "{CE9C8903-AA8A-40E6-B03D-32A08A4A39AF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Ini.Test", "test\Config.Ini.Test\Config.Ini.Test.csproj", "{80A8F10C-E9A6-4677-919D-FE5DB320FEDF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.EnvironmentVariables.Test", "test\Config.EnvironmentVariables.Test\Config.EnvironmentVariables.Test.csproj", "{7D0F805B-ADFF-4C47-A90C-24DD74416821}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Binder", "src\Config.Binder\Config.Binder.csproj", "{D506FD2F-59A0-4A26-AA6D-E81998B58B34}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Binder.Test", "test\Config.Binder.Test\Config.Binder.Test.csproj", "{AE6FFE9B-6378-4D57-AA24-7D257F18B235}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.FileExtensions", "src\Config.FileExtensions\Config.FileExtensions.csproj", "{881E7CBC-492C-47C5-98A6-61DD1C753EE6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.FileExtensions.Test", "test\Config.FileExtensions.Test\Config.FileExtensions.Test.csproj", "{F7932F19-EB68-4C52-9CD1-3B51E48C2337}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.AzureKeyVault", "src\Config.AzureKeyVault\Config.AzureKeyVault.csproj", "{A538F609-E902-40CE-8459-4248F9F63558}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.AzureKeyVault.Test", "test\Config.AzureKeyVault.Test\Config.AzureKeyVault.Test.csproj", "{DA9C1F35-3F92-4F3A-B3B1-A62CCE626D48}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{AB015580-541D-4E2D-B904-E71F8582CC68}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeyVaultSample", "samples\KeyVaultSample\KeyVaultSample.csproj", "{7AAC49A7-939A-40B1-A86A-705464B4667D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.UserSecrets", "src\Config.UserSecrets\Config.UserSecrets.csproj", "{58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.UserSecrets.Test", "test\Config.UserSecrets.Test\Config.UserSecrets.Test.csproj", "{AC7FAD2A-5763-404D-B0FC-3CCA81A16B0A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.KeyPerFile", "src\Config.KeyPerFile\Config.KeyPerFile.csproj", "{69AB0230-D82E-438B-AFE5-85BFF414F1B8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.KeyPerFile.Test", "test\Config.KeyPerFile.Test\Config.KeyPerFile.Test.csproj", "{82A403ED-F827-4FED-BE38-7F87925A07E1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B926267F-98A3-41F0-90F5-0B1ADF78CA65}" ProjectSection(SolutionItems) = preProject .appveyor.yml = .appveyor.yml .travis.yml = .travis.yml build.cmd = build.cmd build.ps1 = build.ps1 build.sh = build.sh CONTRIBUTING.md = CONTRIBUTING.md Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets LICENSE.txt = LICENSE.txt NuGet.config = NuGet.config NuGetPackageVerifier.json = NuGetPackageVerifier.json README.md = README.md version.xml = version.xml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{F786716B-DE4D-4C4D-8D5B-38182329E5B5}" ProjectSection(SolutionItems) = preProject build\dependencies.props = build\dependencies.props build\repo.props = build\repo.props EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {4C4CD1BC-4411-4AFD-9D04-147053F0E259}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4C4CD1BC-4411-4AFD-9D04-147053F0E259}.Debug|Any CPU.Build.0 = Debug|Any CPU {4C4CD1BC-4411-4AFD-9D04-147053F0E259}.Release|Any CPU.ActiveCfg = Release|Any CPU {4C4CD1BC-4411-4AFD-9D04-147053F0E259}.Release|Any CPU.Build.0 = Release|Any CPU {1BEC97C1-56B9-4B2B-A95A-C0DF72F1E96A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1BEC97C1-56B9-4B2B-A95A-C0DF72F1E96A}.Debug|Any CPU.Build.0 = Debug|Any CPU {1BEC97C1-56B9-4B2B-A95A-C0DF72F1E96A}.Release|Any CPU.ActiveCfg = Release|Any CPU {1BEC97C1-56B9-4B2B-A95A-C0DF72F1E96A}.Release|Any CPU.Build.0 = Release|Any CPU {AE8F8C20-9ED9-4A16-9565-27DF77683789}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AE8F8C20-9ED9-4A16-9565-27DF77683789}.Debug|Any CPU.Build.0 = Debug|Any CPU {AE8F8C20-9ED9-4A16-9565-27DF77683789}.Release|Any CPU.ActiveCfg = Release|Any CPU {AE8F8C20-9ED9-4A16-9565-27DF77683789}.Release|Any CPU.Build.0 = Release|Any CPU {0786C785-944A-4423-96A6-4E7BFDB4A1B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0786C785-944A-4423-96A6-4E7BFDB4A1B0}.Debug|Any CPU.Build.0 = Debug|Any CPU {0786C785-944A-4423-96A6-4E7BFDB4A1B0}.Release|Any CPU.ActiveCfg = Release|Any CPU {0786C785-944A-4423-96A6-4E7BFDB4A1B0}.Release|Any CPU.Build.0 = Release|Any CPU {8777C77E-CA2A-42C1-90CD-2EA9CBF28937}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8777C77E-CA2A-42C1-90CD-2EA9CBF28937}.Debug|Any CPU.Build.0 = Debug|Any CPU {8777C77E-CA2A-42C1-90CD-2EA9CBF28937}.Release|Any CPU.ActiveCfg = Release|Any CPU {8777C77E-CA2A-42C1-90CD-2EA9CBF28937}.Release|Any CPU.Build.0 = Release|Any CPU {62BD48B5-BB0C-4C2C-9C4B-04CF75CDCCF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {62BD48B5-BB0C-4C2C-9C4B-04CF75CDCCF1}.Debug|Any CPU.Build.0 = Debug|Any CPU {62BD48B5-BB0C-4C2C-9C4B-04CF75CDCCF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {62BD48B5-BB0C-4C2C-9C4B-04CF75CDCCF1}.Release|Any CPU.Build.0 = Release|Any CPU {EAC77F15-F12E-496B-9184-1B1DA89BFFE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EAC77F15-F12E-496B-9184-1B1DA89BFFE9}.Debug|Any CPU.Build.0 = Debug|Any CPU {EAC77F15-F12E-496B-9184-1B1DA89BFFE9}.Release|Any CPU.ActiveCfg = Release|Any CPU {EAC77F15-F12E-496B-9184-1B1DA89BFFE9}.Release|Any CPU.Build.0 = Release|Any CPU {3F1CB08E-9FBD-4CAE-A78A-4AC43F24FC49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3F1CB08E-9FBD-4CAE-A78A-4AC43F24FC49}.Debug|Any CPU.Build.0 = Debug|Any CPU {3F1CB08E-9FBD-4CAE-A78A-4AC43F24FC49}.Release|Any CPU.ActiveCfg = Release|Any CPU {3F1CB08E-9FBD-4CAE-A78A-4AC43F24FC49}.Release|Any CPU.Build.0 = Release|Any CPU {29C120E5-F682-4BFB-826B-040A594802CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29C120E5-F682-4BFB-826B-040A594802CA}.Debug|Any CPU.Build.0 = Debug|Any CPU {29C120E5-F682-4BFB-826B-040A594802CA}.Release|Any CPU.ActiveCfg = Release|Any CPU {29C120E5-F682-4BFB-826B-040A594802CA}.Release|Any CPU.Build.0 = Release|Any CPU {D4B7CF9B-4229-44DC-800F-CC39150CEAB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D4B7CF9B-4229-44DC-800F-CC39150CEAB2}.Debug|Any CPU.Build.0 = Debug|Any CPU {D4B7CF9B-4229-44DC-800F-CC39150CEAB2}.Release|Any CPU.ActiveCfg = Release|Any CPU {D4B7CF9B-4229-44DC-800F-CC39150CEAB2}.Release|Any CPU.Build.0 = Release|Any CPU {C555C5D5-BF4A-451E-AB43-EBF4DE885EC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C555C5D5-BF4A-451E-AB43-EBF4DE885EC7}.Debug|Any CPU.Build.0 = Debug|Any CPU {C555C5D5-BF4A-451E-AB43-EBF4DE885EC7}.Release|Any CPU.ActiveCfg = Release|Any CPU {C555C5D5-BF4A-451E-AB43-EBF4DE885EC7}.Release|Any CPU.Build.0 = Release|Any CPU {A6A2C665-E5A4-4FD3-AD0C-E33E6CFFCB88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A6A2C665-E5A4-4FD3-AD0C-E33E6CFFCB88}.Debug|Any CPU.Build.0 = Debug|Any CPU {A6A2C665-E5A4-4FD3-AD0C-E33E6CFFCB88}.Release|Any CPU.ActiveCfg = Release|Any CPU {A6A2C665-E5A4-4FD3-AD0C-E33E6CFFCB88}.Release|Any CPU.Build.0 = Release|Any CPU {CE9C8903-AA8A-40E6-B03D-32A08A4A39AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CE9C8903-AA8A-40E6-B03D-32A08A4A39AF}.Debug|Any CPU.Build.0 = Debug|Any CPU {CE9C8903-AA8A-40E6-B03D-32A08A4A39AF}.Release|Any CPU.ActiveCfg = Release|Any CPU {CE9C8903-AA8A-40E6-B03D-32A08A4A39AF}.Release|Any CPU.Build.0 = Release|Any CPU {80A8F10C-E9A6-4677-919D-FE5DB320FEDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {80A8F10C-E9A6-4677-919D-FE5DB320FEDF}.Debug|Any CPU.Build.0 = Debug|Any CPU {80A8F10C-E9A6-4677-919D-FE5DB320FEDF}.Release|Any CPU.ActiveCfg = Release|Any CPU {80A8F10C-E9A6-4677-919D-FE5DB320FEDF}.Release|Any CPU.Build.0 = Release|Any CPU {7D0F805B-ADFF-4C47-A90C-24DD74416821}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7D0F805B-ADFF-4C47-A90C-24DD74416821}.Debug|Any CPU.Build.0 = Debug|Any CPU {7D0F805B-ADFF-4C47-A90C-24DD74416821}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D0F805B-ADFF-4C47-A90C-24DD74416821}.Release|Any CPU.Build.0 = Release|Any CPU {D506FD2F-59A0-4A26-AA6D-E81998B58B34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D506FD2F-59A0-4A26-AA6D-E81998B58B34}.Debug|Any CPU.Build.0 = Debug|Any CPU {D506FD2F-59A0-4A26-AA6D-E81998B58B34}.Release|Any CPU.ActiveCfg = Release|Any CPU {D506FD2F-59A0-4A26-AA6D-E81998B58B34}.Release|Any CPU.Build.0 = Release|Any CPU {AE6FFE9B-6378-4D57-AA24-7D257F18B235}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AE6FFE9B-6378-4D57-AA24-7D257F18B235}.Debug|Any CPU.Build.0 = Debug|Any CPU {AE6FFE9B-6378-4D57-AA24-7D257F18B235}.Release|Any CPU.ActiveCfg = Release|Any CPU {AE6FFE9B-6378-4D57-AA24-7D257F18B235}.Release|Any CPU.Build.0 = Release|Any CPU {881E7CBC-492C-47C5-98A6-61DD1C753EE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {881E7CBC-492C-47C5-98A6-61DD1C753EE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {881E7CBC-492C-47C5-98A6-61DD1C753EE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {881E7CBC-492C-47C5-98A6-61DD1C753EE6}.Release|Any CPU.Build.0 = Release|Any CPU {F7932F19-EB68-4C52-9CD1-3B51E48C2337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F7932F19-EB68-4C52-9CD1-3B51E48C2337}.Debug|Any CPU.Build.0 = Debug|Any CPU {F7932F19-EB68-4C52-9CD1-3B51E48C2337}.Release|Any CPU.ActiveCfg = Release|Any CPU {F7932F19-EB68-4C52-9CD1-3B51E48C2337}.Release|Any CPU.Build.0 = Release|Any CPU {A538F609-E902-40CE-8459-4248F9F63558}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A538F609-E902-40CE-8459-4248F9F63558}.Debug|Any CPU.Build.0 = Debug|Any CPU {A538F609-E902-40CE-8459-4248F9F63558}.Release|Any CPU.ActiveCfg = Release|Any CPU {A538F609-E902-40CE-8459-4248F9F63558}.Release|Any CPU.Build.0 = Release|Any CPU {DA9C1F35-3F92-4F3A-B3B1-A62CCE626D48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DA9C1F35-3F92-4F3A-B3B1-A62CCE626D48}.Debug|Any CPU.Build.0 = Debug|Any CPU {DA9C1F35-3F92-4F3A-B3B1-A62CCE626D48}.Release|Any CPU.ActiveCfg = Release|Any CPU {DA9C1F35-3F92-4F3A-B3B1-A62CCE626D48}.Release|Any CPU.Build.0 = Release|Any CPU {7AAC49A7-939A-40B1-A86A-705464B4667D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7AAC49A7-939A-40B1-A86A-705464B4667D}.Debug|Any CPU.Build.0 = Debug|Any CPU {7AAC49A7-939A-40B1-A86A-705464B4667D}.Release|Any CPU.ActiveCfg = Release|Any CPU {7AAC49A7-939A-40B1-A86A-705464B4667D}.Release|Any CPU.Build.0 = Release|Any CPU {58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Debug|Any CPU.Build.0 = Debug|Any CPU {58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Release|Any CPU.ActiveCfg = Release|Any CPU {58B6443B-1278-4DF9-B7BB-DDF3BFFCF868}.Release|Any CPU.Build.0 = Release|Any CPU {AC7FAD2A-5763-404D-B0FC-3CCA81A16B0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AC7FAD2A-5763-404D-B0FC-3CCA81A16B0A}.Debug|Any CPU.Build.0 = Debug|Any CPU {AC7FAD2A-5763-404D-B0FC-3CCA81A16B0A}.Release|Any CPU.ActiveCfg = Release|Any CPU {AC7FAD2A-5763-404D-B0FC-3CCA81A16B0A}.Release|Any CPU.Build.0 = Release|Any CPU {69AB0230-D82E-438B-AFE5-85BFF414F1B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {69AB0230-D82E-438B-AFE5-85BFF414F1B8}.Debug|Any CPU.Build.0 = Debug|Any CPU {69AB0230-D82E-438B-AFE5-85BFF414F1B8}.Release|Any CPU.ActiveCfg = Release|Any CPU {69AB0230-D82E-438B-AFE5-85BFF414F1B8}.Release|Any CPU.Build.0 = Release|Any CPU {82A403ED-F827-4FED-BE38-7F87925A07E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {82A403ED-F827-4FED-BE38-7F87925A07E1}.Debug|Any CPU.Build.0 = Debug|Any CPU {82A403ED-F827-4FED-BE38-7F87925A07E1}.Release|Any CPU.ActiveCfg = Release|Any CPU {82A403ED-F827-4FED-BE38-7F87925A07E1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {4C4CD1BC-4411-4AFD-9D04-147053F0E259} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {1BEC97C1-56B9-4B2B-A95A-C0DF72F1E96A} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {AE8F8C20-9ED9-4A16-9565-27DF77683789} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {0786C785-944A-4423-96A6-4E7BFDB4A1B0} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {8777C77E-CA2A-42C1-90CD-2EA9CBF28937} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {62BD48B5-BB0C-4C2C-9C4B-04CF75CDCCF1} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {EAC77F15-F12E-496B-9184-1B1DA89BFFE9} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {3F1CB08E-9FBD-4CAE-A78A-4AC43F24FC49} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {29C120E5-F682-4BFB-826B-040A594802CA} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {D4B7CF9B-4229-44DC-800F-CC39150CEAB2} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {C555C5D5-BF4A-451E-AB43-EBF4DE885EC7} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {A6A2C665-E5A4-4FD3-AD0C-E33E6CFFCB88} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {CE9C8903-AA8A-40E6-B03D-32A08A4A39AF} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {80A8F10C-E9A6-4677-919D-FE5DB320FEDF} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {7D0F805B-ADFF-4C47-A90C-24DD74416821} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {D506FD2F-59A0-4A26-AA6D-E81998B58B34} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {AE6FFE9B-6378-4D57-AA24-7D257F18B235} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {881E7CBC-492C-47C5-98A6-61DD1C753EE6} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {F7932F19-EB68-4C52-9CD1-3B51E48C2337} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {A538F609-E902-40CE-8459-4248F9F63558} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {DA9C1F35-3F92-4F3A-B3B1-A62CCE626D48} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {7AAC49A7-939A-40B1-A86A-705464B4667D} = {AB015580-541D-4E2D-B904-E71F8582CC68} {58B6443B-1278-4DF9-B7BB-DDF3BFFCF868} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {AC7FAD2A-5763-404D-B0FC-3CCA81A16B0A} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {69AB0230-D82E-438B-AFE5-85BFF414F1B8} = {F141E2D0-F9B8-4ADB-A19A-7B6FF4CA19A1} {82A403ED-F827-4FED-BE38-7F87925A07E1} = {B54371FF-B920-46C8-8D55-6B19DBB43EBF} {F786716B-DE4D-4C4D-8D5B-38182329E5B5} = {B926267F-98A3-41F0-90F5-0B1ADF78CA65} EndGlobalSection EndGlobal ================================================ FILE: Directory.Build.props ================================================  Microsoft .NET Extensions https://github.com/aspnet/Configuration git $(MSBuildThisFileDirectory) $(MSBuildThisFileDirectory)build\Key.snk true true ================================================ FILE: Directory.Build.targets ================================================ $(MicrosoftNETCoreApp20PackageVersion) $(MicrosoftNETCoreApp21PackageVersion) $(MicrosoftNETCoreApp22PackageVersion) $(NETStandardLibrary20PackageVersion) 99.9 ================================================ FILE: LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright (c) .NET Foundation and Contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: NuGet.config ================================================  ================================================ FILE: NuGetPackageVerifier.json ================================================ { "Default": { "rules": [ "DefaultCompositeRule" ] } } ================================================ FILE: README.md ================================================ Configuration [Archived] ======================== **This GitHub project has been archived.** Ongoing development on this project can be found in . Configuration is a framework for accessing Key/Value based configuration settings in an application. Includes configuration providers for command line arguments, environment variables, INI files, JSON files, and XML files. This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [AspNetCore](https://github.com/aspnet/AspNetCore) repo. ================================================ FILE: build/dependencies.props ================================================  $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 3.0.0-alpha1-20181004.7 3.0.0-alpha1-10584 2.3.2 1.0.1 3.0.0-alpha1-10584 3.0.0-alpha1-10584 2.0.9 2.1.3 2.2.0-preview2-26905-02 15.6.1 4.9.0 2.0.3 11.0.2 4.6.0-preview1-26907-04 2.3.1 2.4.0 ================================================ FILE: build/repo.props ================================================ Internal.AspNetCore.Universe.Lineup https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json ================================================ FILE: build/sources.props ================================================ $(DotNetRestoreSources) $(RestoreSources); https://dotnet.myget.org/F/dotnet-core/api/v3/index.json; https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json; $(RestoreSources); https://api.nuget.org/v3/index.json; ================================================ FILE: build.cmd ================================================ @ECHO OFF PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' default-build %*; exit $LASTEXITCODE" ================================================ FILE: build.sh ================================================ #!/usr/bin/env bash set -euo pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Call "sync" between "chmod" and execution to prevent "text file busy" error in Docker (aufs) chmod +x "$DIR/run.sh"; sync "$DIR/run.sh" default-build "$@" ================================================ FILE: korebuild-lock.txt ================================================ version:3.0.0-alpha1-20181004.7 commithash:27fabdaf2b1d4753c3d2749581694ca65d78f7f2 ================================================ FILE: korebuild.json ================================================ { "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json", "channel": "master" } ================================================ FILE: run.cmd ================================================ @ECHO OFF PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' %*; exit $LASTEXITCODE" ================================================ FILE: run.ps1 ================================================ #!/usr/bin/env powershell #requires -version 4 <# .SYNOPSIS Executes KoreBuild commands. .DESCRIPTION Downloads korebuild if required. Then executes the KoreBuild command. To see available commands, execute with `-Command help`. .PARAMETER Command The KoreBuild command to run. .PARAMETER Path The folder to build. Defaults to the folder containing this script. .PARAMETER Channel The channel of KoreBuild to download. Overrides the value from the config file. .PARAMETER DotNetHome The directory where .NET Core tools will be stored. .PARAMETER ToolsSource The base url where build tools can be downloaded. Overrides the value from the config file. .PARAMETER Update Updates KoreBuild to the latest version even if a lock file is present. .PARAMETER Reinstall Re-installs KoreBuild .PARAMETER ConfigFile The path to the configuration file that stores values. Defaults to korebuild.json. .PARAMETER ToolsSourceSuffix The Suffix to append to the end of the ToolsSource. Useful for query strings in blob stores. .PARAMETER CI Sets up CI specific settings and variables. .PARAMETER Arguments Arguments to be passed to the command .NOTES This function will create a file $PSScriptRoot/korebuild-lock.txt. This lock file can be committed to source, but does not have to be. When the lockfile is not present, KoreBuild will create one using latest available version from $Channel. The $ConfigFile is expected to be an JSON file. It is optional, and the configuration values in it are optional as well. Any options set in the file are overridden by command line parameters. .EXAMPLE Example config file: ```json { "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json", "channel": "master", "toolsSource": "https://aspnetcore.blob.core.windows.net/buildtools" } ``` #> [CmdletBinding(PositionalBinding = $false)] param( [Parameter(Mandatory = $true, Position = 0)] [string]$Command, [string]$Path = $PSScriptRoot, [Alias('c')] [string]$Channel, [Alias('d')] [string]$DotNetHome, [Alias('s')] [string]$ToolsSource, [Alias('u')] [switch]$Update, [switch]$Reinstall, [string]$ToolsSourceSuffix, [string]$ConfigFile = $null, [switch]$CI, [Parameter(ValueFromRemainingArguments = $true)] [string[]]$Arguments ) Set-StrictMode -Version 2 $ErrorActionPreference = 'Stop' # # Functions # function Get-KoreBuild { $lockFile = Join-Path $Path 'korebuild-lock.txt' if (!(Test-Path $lockFile) -or $Update) { Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $lockFile $ToolsSourceSuffix } $version = Get-Content $lockFile | Where-Object { $_ -like 'version:*' } | Select-Object -first 1 if (!$version) { Write-Error "Failed to parse version from $lockFile. Expected a line that begins with 'version:'" } $version = $version.TrimStart('version:').Trim() $korebuildPath = Join-Paths $DotNetHome ('buildtools', 'korebuild', $version) if ($Reinstall -and (Test-Path $korebuildPath)) { Remove-Item -Force -Recurse $korebuildPath } if (!(Test-Path $korebuildPath)) { Write-Host -ForegroundColor Magenta "Downloading KoreBuild $version" New-Item -ItemType Directory -Path $korebuildPath | Out-Null $remotePath = "$ToolsSource/korebuild/artifacts/$version/korebuild.$version.zip" try { $tmpfile = Join-Path ([IO.Path]::GetTempPath()) "KoreBuild-$([guid]::NewGuid()).zip" Get-RemoteFile $remotePath $tmpfile $ToolsSourceSuffix if (Get-Command -Name 'Microsoft.PowerShell.Archive\Expand-Archive' -ErrorAction Ignore) { # Use built-in commands where possible as they are cross-plat compatible Microsoft.PowerShell.Archive\Expand-Archive -Path $tmpfile -DestinationPath $korebuildPath } else { # Fallback to old approach for old installations of PowerShell Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory($tmpfile, $korebuildPath) } } catch { Remove-Item -Recurse -Force $korebuildPath -ErrorAction Ignore throw } finally { Remove-Item $tmpfile -ErrorAction Ignore } } return $korebuildPath } function Join-Paths([string]$path, [string[]]$childPaths) { $childPaths | ForEach-Object { $path = Join-Path $path $_ } return $path } function Get-RemoteFile([string]$RemotePath, [string]$LocalPath, [string]$RemoteSuffix) { if ($RemotePath -notlike 'http*') { Copy-Item $RemotePath $LocalPath return } $retries = 10 while ($retries -gt 0) { $retries -= 1 try { Invoke-WebRequest -UseBasicParsing -Uri $($RemotePath + $RemoteSuffix) -OutFile $LocalPath return } catch { Write-Verbose "Request failed. $retries retries remaining" } } Write-Error "Download failed: '$RemotePath'." } # # Main # # Load configuration or set defaults $Path = Resolve-Path $Path if (!$ConfigFile) { $ConfigFile = Join-Path $Path 'korebuild.json' } if (Test-Path $ConfigFile) { try { $config = Get-Content -Raw -Encoding UTF8 -Path $ConfigFile | ConvertFrom-Json if ($config) { if (!($Channel) -and (Get-Member -Name 'channel' -InputObject $config)) { [string] $Channel = $config.channel } if (!($ToolsSource) -and (Get-Member -Name 'toolsSource' -InputObject $config)) { [string] $ToolsSource = $config.toolsSource} } } catch { Write-Host -ForegroundColor Red $Error[0] Write-Error "$ConfigFile contains invalid JSON." exit 1 } } if (!$DotNetHome) { $DotNetHome = if ($env:DOTNET_HOME) { $env:DOTNET_HOME } ` elseif ($env:USERPROFILE) { Join-Path $env:USERPROFILE '.dotnet'} ` elseif ($env:HOME) {Join-Path $env:HOME '.dotnet'}` else { Join-Path $PSScriptRoot '.dotnet'} } if (!$Channel) { $Channel = 'master' } if (!$ToolsSource) { $ToolsSource = 'https://aspnetcore.blob.core.windows.net/buildtools' } # Execute $korebuildPath = Get-KoreBuild Import-Module -Force -Scope Local (Join-Path $korebuildPath 'KoreBuild.psd1') try { Set-KoreBuildSettings -ToolsSource $ToolsSource -DotNetHome $DotNetHome -RepoPath $Path -ConfigFile $ConfigFile -CI:$CI Invoke-KoreBuildCommand $Command @Arguments } finally { Remove-Module 'KoreBuild' -ErrorAction Ignore } ================================================ FILE: run.sh ================================================ #!/usr/bin/env bash set -euo pipefail # # variables # RESET="\033[0m" RED="\033[0;31m" YELLOW="\033[0;33m" MAGENTA="\033[0;95m" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" [ -z "${DOTNET_HOME:-}" ] && DOTNET_HOME="$HOME/.dotnet" verbose=false update=false reinstall=false repo_path="$DIR" channel='' tools_source='' tools_source_suffix='' ci=false # # Functions # __usage() { echo "Usage: $(basename "${BASH_SOURCE[0]}") command [options] [[--] ...]" echo "" echo "Arguments:" echo " command The command to be run." echo " ... Arguments passed to the command. Variable number of arguments allowed." echo "" echo "Options:" echo " --verbose Show verbose output." echo " -c|--channel The channel of KoreBuild to download. Overrides the value from the config file.." echo " --config-file The path to the configuration file that stores values. Defaults to korebuild.json." echo " -d|--dotnet-home The directory where .NET Core tools will be stored. Defaults to '\$DOTNET_HOME' or '\$HOME/.dotnet." echo " --path The directory to build. Defaults to the directory containing the script." echo " -s|--tools-source|-ToolsSource The base url where build tools can be downloaded. Overrides the value from the config file." echo " --tools-source-suffix|-ToolsSourceSuffix The suffix to append to tools-source. Useful for query strings." echo " -u|--update Update to the latest KoreBuild even if the lock file is present." echo " --reinstall Reinstall KoreBuild." echo " --ci Apply CI specific settings and environment variables." echo "" echo "Description:" echo " This function will create a file \$DIR/korebuild-lock.txt. This lock file can be committed to source, but does not have to be." echo " When the lockfile is not present, KoreBuild will create one using latest available version from \$channel." if [[ "${1:-}" != '--no-exit' ]]; then exit 2 fi } get_korebuild() { local version local lock_file="$repo_path/korebuild-lock.txt" if [ ! -f "$lock_file" ] || [ "$update" = true ]; then __get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lock_file" "$tools_source_suffix" fi version="$(grep 'version:*' -m 1 "$lock_file")" if [[ "$version" == '' ]]; then __error "Failed to parse version from $lock_file. Expected a line that begins with 'version:'" return 1 fi version="$(echo "${version#version:}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" local korebuild_path="$DOTNET_HOME/buildtools/korebuild/$version" if [ "$reinstall" = true ] && [ -d "$korebuild_path" ]; then rm -rf "$korebuild_path" fi { if [ ! -d "$korebuild_path" ]; then mkdir -p "$korebuild_path" local remote_path="$tools_source/korebuild/artifacts/$version/korebuild.$version.zip" tmpfile="$(mktemp)" echo -e "${MAGENTA}Downloading KoreBuild ${version}${RESET}" if __get_remote_file "$remote_path" "$tmpfile" "$tools_source_suffix"; then unzip -q -d "$korebuild_path" "$tmpfile" fi rm "$tmpfile" || true fi source "$korebuild_path/KoreBuild.sh" } || { if [ -d "$korebuild_path" ]; then echo "Cleaning up after failed installation" rm -rf "$korebuild_path" || true fi return 1 } } __error() { echo -e "${RED}error: $*${RESET}" 1>&2 } __warn() { echo -e "${YELLOW}warning: $*${RESET}" } __machine_has() { hash "$1" > /dev/null 2>&1 return $? } __get_remote_file() { local remote_path=$1 local local_path=$2 local remote_path_suffix=$3 if [[ "$remote_path" != 'http'* ]]; then cp "$remote_path" "$local_path" return 0 fi local failed=false if __machine_has wget; then wget --tries 10 --quiet -O "$local_path" "${remote_path}${remote_path_suffix}" || failed=true else failed=true fi if [ "$failed" = true ] && __machine_has curl; then failed=false curl --retry 10 -sSL -f --create-dirs -o "$local_path" "${remote_path}${remote_path_suffix}" || failed=true fi if [ "$failed" = true ]; then __error "Download failed: $remote_path" 1>&2 return 1 fi } # # main # command="${1:-}" shift while [[ $# -gt 0 ]]; do case $1 in -\?|-h|--help) __usage --no-exit exit 0 ;; -c|--channel|-Channel) shift channel="${1:-}" [ -z "$channel" ] && __usage ;; --config-file|-ConfigFile) shift config_file="${1:-}" [ -z "$config_file" ] && __usage if [ ! -f "$config_file" ]; then __error "Invalid value for --config-file. $config_file does not exist." exit 1 fi ;; -d|--dotnet-home|-DotNetHome) shift DOTNET_HOME="${1:-}" [ -z "$DOTNET_HOME" ] && __usage ;; --path|-Path) shift repo_path="${1:-}" [ -z "$repo_path" ] && __usage ;; -s|--tools-source|-ToolsSource) shift tools_source="${1:-}" [ -z "$tools_source" ] && __usage ;; --tools-source-suffix|-ToolsSourceSuffix) shift tools_source_suffix="${1:-}" [ -z "$tools_source_suffix" ] && __usage ;; -u|--update|-Update) update=true ;; --reinstall|-[Rr]einstall) reinstall=true ;; --ci|-[Cc][Ii]) ci=true ;; --verbose|-Verbose) verbose=true ;; --) shift break ;; *) break ;; esac shift done if ! __machine_has unzip; then __error 'Missing required command: unzip' exit 1 fi if ! __machine_has curl && ! __machine_has wget; then __error 'Missing required command. Either wget or curl is required.' exit 1 fi [ -z "${config_file:-}" ] && config_file="$repo_path/korebuild.json" if [ -f "$config_file" ]; then if __machine_has jq ; then if jq '.' "$config_file" >/dev/null ; then config_channel="$(jq -r 'select(.channel!=null) | .channel' "$config_file")" config_tools_source="$(jq -r 'select(.toolsSource!=null) | .toolsSource' "$config_file")" else __error "$config_file contains invalid JSON." exit 1 fi elif __machine_has python ; then if python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then config_channel="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" config_tools_source="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" else __error "$config_file contains invalid JSON." exit 1 fi elif __machine_has python3 ; then if python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then config_channel="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" config_tools_source="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" else __error "$config_file contains invalid JSON." exit 1 fi else __error 'Missing required command: jq or python. Could not parse the JSON file.' exit 1 fi [ ! -z "${config_channel:-}" ] && channel="$config_channel" [ ! -z "${config_tools_source:-}" ] && tools_source="$config_tools_source" fi [ -z "$channel" ] && channel='master' [ -z "$tools_source" ] && tools_source='https://aspnetcore.blob.core.windows.net/buildtools' get_korebuild set_korebuildsettings "$tools_source" "$DOTNET_HOME" "$repo_path" "$config_file" "$ci" invoke_korebuild_command "$command" "$@" ================================================ FILE: samples/KeyVaultSample/EnvironmentSecretManager.cs ================================================ using Microsoft.Azure.KeyVault.Models; using Microsoft.Extensions.Configuration.AzureKeyVault; namespace ConsoleApplication { public class EnvironmentSecretManager : DefaultKeyVaultSecretManager { private readonly string _environmentPrefix; public EnvironmentSecretManager(string environment) { _environmentPrefix = environment + "-"; } public override bool Load(SecretItem secret) { return HasEnvironmentPrefix(secret.Identifier.Name); } public override string GetKey(SecretBundle secret) { var keyName = base.GetKey(secret); return HasEnvironmentPrefix(keyName) ? keyName.Substring(_environmentPrefix.Length) : keyName; } private bool HasEnvironmentPrefix(string name) { return name.StartsWith(_environmentPrefix); } } } ================================================ FILE: samples/KeyVaultSample/KeyVaultSample.csproj ================================================ netcoreapp2.2;net461 portable Exe ================================================ FILE: samples/KeyVaultSample/Program.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography.X509Certificates; using Microsoft.Extensions.Configuration; namespace ConsoleApplication { public class Program { public static void Main(string[] args) { var builder = new ConfigurationBuilder(); builder.AddJsonFile("settings.json"); var config = builder.Build(); var store = new X509Store(StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly); var cert = store.Certificates.Find(X509FindType.FindByThumbprint, config["CertificateThumbprint"], false); builder.AddAzureKeyVault( config["Vault"], config["ClientId"], cert.OfType().Single(), new EnvironmentSecretManager("Development")); store.Close(); config = builder.Build(); Console.WriteLine(config["ConnectionString"]); } } } ================================================ FILE: samples/KeyVaultSample/settings.json ================================================ { "CertificateThumbprint": "", "Vault": "", "ClientId": "" } ================================================ FILE: src/Config/ChainedBuilderExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; namespace Microsoft.Extensions.Configuration { /// /// IConfigurationBuilder extension methods for the chaind configuration provider. /// public static class ChainedBuilderExtensions { /// /// Adds an existing configuration to . /// /// The to add to. /// The to add. /// The . public static IConfigurationBuilder AddConfiguration(this IConfigurationBuilder configurationBuilder, IConfiguration config) { if (configurationBuilder == null) { throw new ArgumentNullException(nameof(configurationBuilder)); } if (config == null) { throw new ArgumentNullException(nameof(config)); } configurationBuilder.Add(new ChainedConfigurationSource { Configuration = config }); return configurationBuilder; } } } ================================================ FILE: src/Config/ChainedConfigurationProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Primitives; namespace Microsoft.Extensions.Configuration { /// /// Chained implementation of /// public class ChainedConfigurationProvider : IConfigurationProvider { private readonly IConfiguration _config; /// /// Initialize a new instance from the source configuration. /// /// The source configuration. public ChainedConfigurationProvider(ChainedConfigurationSource source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (source.Configuration == null) { throw new ArgumentNullException(nameof(source.Configuration)); } _config = source.Configuration; } /// /// Tries to get a configuration value for the specified key. /// /// The key. /// The value. /// True if a value for the specified key was found, otherwise false. public bool TryGet(string key, out string value) { value = _config[key]; return !string.IsNullOrEmpty(value); } /// /// Sets a configuration value for the specified key. /// /// The key. /// The value. public void Set(string key, string value) => _config[key] = value; /// /// Returns a change token if this provider supports change tracking, null otherwise. /// /// public IChangeToken GetReloadToken() => _config.GetReloadToken(); /// /// Loads configuration values from the source represented by this . /// public void Load() { } /// /// Returns the immediate descendant configuration keys for a given parent path based on this /// 's data and the set of keys returned by all the preceding /// s. /// /// The child keys returned by the preceding providers for the same parent path. /// The parent path. /// The child keys. public IEnumerable GetChildKeys( IEnumerable earlierKeys, string parentPath) { var section = parentPath == null ? _config : _config.GetSection(parentPath); var children = section.GetChildren(); var keys = new List(); keys.AddRange(children.Select(c => c.Key)); return keys.Concat(earlierKeys) .OrderBy(k => k, ConfigurationKeyComparer.Instance); } } } ================================================ FILE: src/Config/ChainedConfigurationSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Extensions.Configuration { /// /// Represents a chained IConfiguration as an . /// public class ChainedConfigurationSource : IConfigurationSource { /// /// The chained configuration. /// public IConfiguration Configuration { get; set; } /// /// Builds the for this source. /// /// The . /// A public IConfigurationProvider Build(IConfigurationBuilder builder) => new ChainedConfigurationProvider(this); } } ================================================ FILE: src/Config/Config.csproj ================================================  Microsoft.Extensions.Configuration Microsoft.Extensions.Configuration Implementation of key-value pair based configuration for Microsoft.Extensions.Configuration. Includes the memory configuration provider. netstandard2.0 ================================================ FILE: src/Config/ConfigurationBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; namespace Microsoft.Extensions.Configuration { /// /// Used to build key/value based configuration settings for use in an application. /// public class ConfigurationBuilder : IConfigurationBuilder { /// /// Returns the sources used to obtain configuration values. /// public IList Sources { get; } = new List(); /// /// Gets a key/value collection that can be used to share data between the /// and the registered s. /// public IDictionary Properties { get; } = new Dictionary(); /// /// Adds a new configuration source. /// /// The configuration source to add. /// The same . public IConfigurationBuilder Add(IConfigurationSource source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } Sources.Add(source); return this; } /// /// Builds an with keys and values from the set of providers registered in /// . /// /// An with keys and values from the registered providers. public IConfigurationRoot Build() { var providers = new List(); foreach (var source in Sources) { var provider = source.Build(this); providers.Add(provider); } return new ConfigurationRoot(providers); } } } ================================================ FILE: src/Config/ConfigurationKeyComparer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; namespace Microsoft.Extensions.Configuration { /// /// IComparer implementation used to order configuration keys. /// public class ConfigurationKeyComparer : IComparer { private static readonly string[] _keyDelimiterArray = new[] { ConfigurationPath.KeyDelimiter }; /// /// The default instance. /// public static ConfigurationKeyComparer Instance { get; } = new ConfigurationKeyComparer(); /// /// Compares two strings. /// /// First string. /// Second string. /// public int Compare(string x, string y) { var xParts = x?.Split(_keyDelimiterArray, StringSplitOptions.RemoveEmptyEntries) ?? new string[0]; var yParts = y?.Split(_keyDelimiterArray, StringSplitOptions.RemoveEmptyEntries) ?? new string[0]; // Compare each part until we get two parts that are not equal for (int i = 0; i < Math.Min(xParts.Length, yParts.Length); i++) { x = xParts[i]; y = yParts[i]; var value1 = 0; var value2 = 0; var xIsInt = x != null && int.TryParse(x, out value1); var yIsInt = y != null && int.TryParse(y, out value2); int result = 0; if (!xIsInt && !yIsInt) { // Both are strings result = string.Compare(x, y, StringComparison.OrdinalIgnoreCase); } else if (xIsInt && yIsInt) { // Both are int result = value1 - value2; } else { // Only one of them is int result = xIsInt ? -1 : 1; } if (result != 0) { // One of them is different return result; } } // If we get here, the common parts are equal. // If they are of the same length, then they are totally identical return xParts.Length - yParts.Length; } } } ================================================ FILE: src/Config/ConfigurationProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.Extensions.Primitives; namespace Microsoft.Extensions.Configuration { /// /// Base helper class for implementing an /// public abstract class ConfigurationProvider : IConfigurationProvider { private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken(); /// /// Initializes a new /// protected ConfigurationProvider() { Data = new Dictionary(StringComparer.OrdinalIgnoreCase); } /// /// The configuration key value pairs for this provider. /// protected IDictionary Data { get; set; } /// /// Attempts to find a value with the given key, returns true if one is found, false otherwise. /// /// The key to lookup. /// The value found at key if one is found. /// True if key has a value, false otherwise. public virtual bool TryGet(string key, out string value) => Data.TryGetValue(key, out value); /// /// Sets a value for a given key. /// /// The configuration key to set. /// The value to set. public virtual void Set(string key, string value) => Data[key] = value; /// /// Loads (or reloads) the data for this provider. /// public virtual void Load() { } /// /// Returns the list of keys that this provider has. /// /// The earlier keys that other providers contain. /// The path for the parent IConfiguration. /// The list of keys for this provider. public virtual IEnumerable GetChildKeys( IEnumerable earlierKeys, string parentPath) { var prefix = parentPath == null ? string.Empty : parentPath + ConfigurationPath.KeyDelimiter; return Data .Where(kv => kv.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) .Select(kv => Segment(kv.Key, prefix.Length)) .Concat(earlierKeys) .OrderBy(k => k, ConfigurationKeyComparer.Instance); } private static string Segment(string key, int prefixLength) { var indexOf = key.IndexOf(ConfigurationPath.KeyDelimiter, prefixLength, StringComparison.OrdinalIgnoreCase); return indexOf < 0 ? key.Substring(prefixLength) : key.Substring(prefixLength, indexOf - prefixLength); } /// /// Returns a that can be used to listen when this provider is reloaded. /// /// public IChangeToken GetReloadToken() { return _reloadToken; } /// /// Triggers the reload change token and creates a new one. /// protected void OnReload() { var previousToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken()); previousToken.OnReload(); } } } ================================================ FILE: src/Config/ConfigurationReloadToken.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Threading; using Microsoft.Extensions.Primitives; namespace Microsoft.Extensions.Configuration { /// /// Implements /// public class ConfigurationReloadToken : IChangeToken { private CancellationTokenSource _cts = new CancellationTokenSource(); /// /// Indicates if this token will proactively raise callbacks. Callbacks are still guaranteed to be invoked, eventually. /// public bool ActiveChangeCallbacks => true; /// /// Gets a value that indicates if a change has occurred. /// public bool HasChanged => _cts.IsCancellationRequested; /// /// Registers for a callback that will be invoked when the entry has changed. /// MUST be set before the callback is invoked. /// /// The callback to invoke. /// State to be passed into the callback. /// public IDisposable RegisterChangeCallback(Action callback, object state) => _cts.Token.Register(callback, state); /// /// Used to trigger the change token when a reload occurs. /// public void OnReload() => _cts.Cancel(); } } ================================================ FILE: src/Config/ConfigurationRoot.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.Extensions.Primitives; namespace Microsoft.Extensions.Configuration { /// /// The root node for a configuration. /// public class ConfigurationRoot : IConfigurationRoot { private IList _providers; private ConfigurationReloadToken _changeToken = new ConfigurationReloadToken(); /// /// Initializes a Configuration root with a list of providers. /// /// The s for this configuration. public ConfigurationRoot(IList providers) { if (providers == null) { throw new ArgumentNullException(nameof(providers)); } _providers = providers; foreach (var p in providers) { p.Load(); ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged()); } } /// /// The s for this configuration. /// public IEnumerable Providers => _providers; /// /// Gets or sets the value corresponding to a configuration key. /// /// The configuration key. /// The configuration value. public string this[string key] { get { foreach (var provider in _providers.Reverse()) { string value; if (provider.TryGet(key, out value)) { return value; } } return null; } set { if (!_providers.Any()) { throw new InvalidOperationException(Resources.Error_NoSources); } foreach (var provider in _providers) { provider.Set(key, value); } } } /// /// Gets the immediate children sub-sections. /// /// public IEnumerable GetChildren() => GetChildrenImplementation(null); internal IEnumerable GetChildrenImplementation(string path) { return _providers .Aggregate(Enumerable.Empty(), (seed, source) => source.GetChildKeys(seed, path)) .Distinct() .Select(key => GetSection(path == null ? key : ConfigurationPath.Combine(path, key))); } /// /// Returns a that can be used to observe when this configuration is reloaded. /// /// public IChangeToken GetReloadToken() => _changeToken; /// /// Gets a configuration sub-section with the specified key. /// /// The key of the configuration section. /// The . /// /// This method will never return null. If no matching sub-section is found with the specified key, /// an empty will be returned. /// public IConfigurationSection GetSection(string key) => new ConfigurationSection(this, key); /// /// Force the configuration values to be reloaded from the underlying sources. /// public void Reload() { foreach (var provider in _providers) { provider.Load(); } RaiseChanged(); } private void RaiseChanged() { var previousToken = Interlocked.Exchange(ref _changeToken, new ConfigurationReloadToken()); previousToken.OnReload(); } } } ================================================ FILE: src/Config/ConfigurationSection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Microsoft.Extensions.Primitives; namespace Microsoft.Extensions.Configuration { /// /// Represents a section of application configuration values. /// public class ConfigurationSection : IConfigurationSection { private readonly ConfigurationRoot _root; private readonly string _path; private string _key; /// /// Initializes a new instance. /// /// The configuration root. /// The path to this section. public ConfigurationSection(ConfigurationRoot root, string path) { if (root == null) { throw new ArgumentNullException(nameof(root)); } if (path == null) { throw new ArgumentNullException(nameof(path)); } _root = root; _path = path; } /// /// Gets the full path to this section from the . /// public string Path => _path; /// /// Gets the key this section occupies in its parent. /// public string Key { get { if (_key == null) { // Key is calculated lazily as last portion of Path _key = ConfigurationPath.GetSectionKey(_path); } return _key; } } /// /// Gets or sets the section value. /// public string Value { get { return _root[Path]; } set { _root[Path] = value; } } /// /// Gets or sets the value corresponding to a configuration key. /// /// The configuration key. /// The configuration value. public string this[string key] { get { return _root[ConfigurationPath.Combine(Path, key)]; } set { _root[ConfigurationPath.Combine(Path, key)] = value; } } /// /// Gets a configuration sub-section with the specified key. /// /// The key of the configuration section. /// The . /// /// This method will never return null. If no matching sub-section is found with the specified key, /// an empty will be returned. /// public IConfigurationSection GetSection(string key) => _root.GetSection(ConfigurationPath.Combine(Path, key)); /// /// Gets the immediate descendant configuration sub-sections. /// /// The configuration sub-sections. public IEnumerable GetChildren() => _root.GetChildrenImplementation(Path); /// /// Returns a that can be used to observe when this configuration is reloaded. /// /// public IChangeToken GetReloadToken() => _root.GetReloadToken(); } } ================================================ FILE: src/Config/MemoryConfigurationBuilderExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Microsoft.Extensions.Configuration.Memory; namespace Microsoft.Extensions.Configuration { /// /// IConfigurationBuilder extension methods for the MemoryConfigurationProvider. /// public static class MemoryConfigurationBuilderExtensions { /// /// Adds the memory configuration provider to . /// /// The to add to. /// The . public static IConfigurationBuilder AddInMemoryCollection(this IConfigurationBuilder configurationBuilder) { if (configurationBuilder == null) { throw new ArgumentNullException(nameof(configurationBuilder)); } configurationBuilder.Add(new MemoryConfigurationSource()); return configurationBuilder; } /// /// Adds the memory configuration provider to . /// /// The to add to. /// The data to add to memory configuration provider. /// The . public static IConfigurationBuilder AddInMemoryCollection( this IConfigurationBuilder configurationBuilder, IEnumerable> initialData) { if (configurationBuilder == null) { throw new ArgumentNullException(nameof(configurationBuilder)); } configurationBuilder.Add(new MemoryConfigurationSource { InitialData = initialData }); return configurationBuilder; } } } ================================================ FILE: src/Config/MemoryConfigurationProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; namespace Microsoft.Extensions.Configuration.Memory { /// /// In-memory implementation of /// public class MemoryConfigurationProvider : ConfigurationProvider, IEnumerable> { private readonly MemoryConfigurationSource _source; /// /// Initialize a new instance from the source. /// /// The source settings. public MemoryConfigurationProvider(MemoryConfigurationSource source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } _source = source; if (_source.InitialData != null) { foreach (var pair in _source.InitialData) { Data.Add(pair.Key, pair.Value); } } } /// /// Add a new key and value pair. /// /// The configuration key. /// The configuration value. public void Add(string key, string value) { Data.Add(key, value); } /// /// Returns an enumerator that iterates through the collection. /// /// An enumerator that can be used to iterate through the collection. public IEnumerator> GetEnumerator() { return Data.GetEnumerator(); } /// /// Returns an enumerator that iterates through the collection. /// /// An enumerator that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: src/Config/MemoryConfigurationSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace Microsoft.Extensions.Configuration.Memory { /// /// Represents in-memory data as an . /// public class MemoryConfigurationSource : IConfigurationSource { /// /// The initial key value configuration pairs. /// public IEnumerable> InitialData { get; set; } /// /// Builds the for this source. /// /// The . /// A public IConfigurationProvider Build(IConfigurationBuilder builder) { return new MemoryConfigurationProvider(this); } } } ================================================ FILE: src/Config/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.Extensions.Configuration.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] ================================================ FILE: src/Config/Properties/Resources.Designer.cs ================================================ // namespace Microsoft.Extensions.Configuration { using System.Globalization; using System.Reflection; using System.Resources; internal static class Resources { private static readonly ResourceManager _resourceManager = new ResourceManager("Microsoft.Extensions.Configuration.Resources", typeof(Resources).GetTypeInfo().Assembly); /// /// A configuration source is not registered. Please register one before setting a value. /// internal static string Error_NoSources { get => GetString("Error_NoSources"); } /// /// A configuration source is not registered. Please register one before setting a value. /// internal static string FormatError_NoSources() => GetString("Error_NoSources"); private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); System.Diagnostics.Debug.Assert(value != null); if (formatterNames != null) { for (var i = 0; i < formatterNames.Length; i++) { value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); } } return value; } } } ================================================ FILE: src/Config/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 A configuration source is not registered. Please register one before setting a value. ================================================ FILE: src/Config/baseline.netcore.json ================================================ { "AssemblyIdentity": "Microsoft.Extensions.Configuration, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.Configuration.ChainedBuilderExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "AddConfiguration", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "config", "Type": "Microsoft.Extensions.Configuration.IConfiguration" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.ChainedConfigurationProvider", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfigurationProvider" ], "Members": [ { "Kind": "Method", "Name": "TryGet", "Parameters": [ { "Name": "key", "Type": "System.String" }, { "Name": "value", "Type": "System.String", "Direction": "Out" } ], "ReturnType": "System.Boolean", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Set", "Parameters": [ { "Name": "key", "Type": "System.String" }, { "Name": "value", "Type": "System.String" } ], "ReturnType": "System.Void", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetReloadToken", "Parameters": [], "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Load", "Parameters": [], "ReturnType": "System.Void", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetChildKeys", "Parameters": [ { "Name": "earlierKeys", "Type": "System.Collections.Generic.IEnumerable" }, { "Name": "parentPath", "Type": "System.String" } ], "ReturnType": "System.Collections.Generic.IEnumerable", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { "Name": "source", "Type": "Microsoft.Extensions.Configuration.ChainedConfigurationSource" } ], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.ChainedConfigurationSource", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfigurationSource" ], "Members": [ { "Kind": "Method", "Name": "get_Configuration", "Parameters": [], "ReturnType": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Configuration", "Parameters": [ { "Name": "value", "Type": "Microsoft.Extensions.Configuration.IConfiguration" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Build", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSource", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.ConfigurationBuilder", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfigurationBuilder" ], "Members": [ { "Kind": "Method", "Name": "get_Sources", "Parameters": [], "ReturnType": "System.Collections.Generic.IList", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Properties", "Parameters": [], "ReturnType": "System.Collections.Generic.IDictionary", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Add", "Parameters": [ { "Name": "source", "Type": "Microsoft.Extensions.Configuration.IConfigurationSource" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Build", "Parameters": [], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationRoot", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.ConfigurationKeyComparer", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [ "System.Collections.Generic.IComparer" ], "Members": [ { "Kind": "Method", "Name": "get_Instance", "Parameters": [], "ReturnType": "Microsoft.Extensions.Configuration.ConfigurationKeyComparer", "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Compare", "Parameters": [ { "Name": "x", "Type": "System.String" }, { "Name": "y", "Type": "System.String" } ], "ReturnType": "System.Int32", "Sealed": true, "Virtual": true, "ImplementedInterface": "System.Collections.Generic.IComparer", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.ConfigurationProvider", "Visibility": "Public", "Kind": "Class", "Abstract": true, "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfigurationProvider" ], "Members": [ { "Kind": "Method", "Name": "get_Data", "Parameters": [], "ReturnType": "System.Collections.Generic.IDictionary", "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Data", "Parameters": [ { "Name": "value", "Type": "System.Collections.Generic.IDictionary" } ], "ReturnType": "System.Void", "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", "Name": "TryGet", "Parameters": [ { "Name": "key", "Type": "System.String" }, { "Name": "value", "Type": "System.String", "Direction": "Out" } ], "ReturnType": "System.Boolean", "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Set", "Parameters": [ { "Name": "key", "Type": "System.String" }, { "Name": "value", "Type": "System.String" } ], "ReturnType": "System.Void", "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Load", "Parameters": [], "ReturnType": "System.Void", "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetChildKeys", "Parameters": [ { "Name": "earlierKeys", "Type": "System.Collections.Generic.IEnumerable" }, { "Name": "parentPath", "Type": "System.String" } ], "ReturnType": "System.Collections.Generic.IEnumerable", "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetReloadToken", "Parameters": [], "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "OnReload", "Parameters": [], "ReturnType": "System.Void", "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Protected", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.ConfigurationReloadToken", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [ "Microsoft.Extensions.Primitives.IChangeToken" ], "Members": [ { "Kind": "Method", "Name": "get_ActiveChangeCallbacks", "Parameters": [], "ReturnType": "System.Boolean", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Primitives.IChangeToken", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_HasChanged", "Parameters": [], "ReturnType": "System.Boolean", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Primitives.IChangeToken", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "RegisterChangeCallback", "Parameters": [ { "Name": "callback", "Type": "System.Action" }, { "Name": "state", "Type": "System.Object" } ], "ReturnType": "System.IDisposable", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Primitives.IChangeToken", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "OnReload", "Parameters": [], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.ConfigurationRoot", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfigurationRoot" ], "Members": [ { "Kind": "Method", "Name": "get_Item", "Parameters": [ { "Name": "key", "Type": "System.String" } ], "ReturnType": "System.String", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Item", "Parameters": [ { "Name": "key", "Type": "System.String" }, { "Name": "value", "Type": "System.String" } ], "ReturnType": "System.Void", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetSection", "Parameters": [ { "Name": "key", "Type": "System.String" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationSection", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetChildren", "Parameters": [], "ReturnType": "System.Collections.Generic.IEnumerable", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetReloadToken", "Parameters": [], "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Providers", "Parameters": [], "ReturnType": "System.Collections.Generic.IEnumerable", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationRoot", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Reload", "Parameters": [], "ReturnType": "System.Void", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationRoot", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { "Name": "providers", "Type": "System.Collections.Generic.IList" } ], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.ConfigurationSection", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfigurationSection" ], "Members": [ { "Kind": "Method", "Name": "get_Item", "Parameters": [ { "Name": "key", "Type": "System.String" } ], "ReturnType": "System.String", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Item", "Parameters": [ { "Name": "key", "Type": "System.String" }, { "Name": "value", "Type": "System.String" } ], "ReturnType": "System.Void", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetSection", "Parameters": [ { "Name": "key", "Type": "System.String" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationSection", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetChildren", "Parameters": [], "ReturnType": "System.Collections.Generic.IEnumerable", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetReloadToken", "Parameters": [], "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Path", "Parameters": [], "ReturnType": "System.String", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSection", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Key", "Parameters": [], "ReturnType": "System.String", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSection", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Value", "Parameters": [], "ReturnType": "System.String", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSection", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Value", "Parameters": [ { "Name": "value", "Type": "System.String" } ], "ReturnType": "System.Void", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSection", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { "Name": "root", "Type": "Microsoft.Extensions.Configuration.ConfigurationRoot" }, { "Name": "path", "Type": "System.String" } ], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.MemoryConfigurationBuilderExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "AddInMemoryCollection", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddInMemoryCollection", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "initialData", "Type": "System.Collections.Generic.IEnumerable>" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.Memory.MemoryConfigurationProvider", "Visibility": "Public", "Kind": "Class", "BaseType": "Microsoft.Extensions.Configuration.ConfigurationProvider", "ImplementedInterfaces": [ "System.Collections.Generic.IEnumerable>" ], "Members": [ { "Kind": "Method", "Name": "Add", "Parameters": [ { "Name": "key", "Type": "System.String" }, { "Name": "value", "Type": "System.String" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetEnumerator", "Parameters": [], "ReturnType": "System.Collections.Generic.IEnumerator>", "Sealed": true, "Virtual": true, "ImplementedInterface": "System.Collections.Generic.IEnumerable>", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { "Name": "source", "Type": "Microsoft.Extensions.Configuration.Memory.MemoryConfigurationSource" } ], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.Memory.MemoryConfigurationSource", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfigurationSource" ], "Members": [ { "Kind": "Method", "Name": "get_InitialData", "Parameters": [], "ReturnType": "System.Collections.Generic.IEnumerable>", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_InitialData", "Parameters": [ { "Name": "value", "Type": "System.Collections.Generic.IEnumerable>" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Build", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSource", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] } ] } ================================================ FILE: src/Config.Abstractions/Config.Abstractions.csproj ================================================  Microsoft.Extensions.Configuration.Abstractions Microsoft.Extensions.Configuration.Abstractions Abstractions of key-value pair based configuration. Commonly used types: Microsoft.Extensions.Configuration.IConfiguration Microsoft.Extensions.Configuration.IConfigurationBuilder Microsoft.Extensions.Configuration.IConfigurationProvider Microsoft.Extensions.Configuration.IConfigurationRoot Microsoft.Extensions.Configuration.IConfigurationSection netstandard2.0 ================================================ FILE: src/Config.Abstractions/ConfigurationExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; namespace Microsoft.Extensions.Configuration { /// /// Extension methods for configuration classes./>. /// public static class ConfigurationExtensions { /// /// Adds a new configuration source. /// /// The to add to. /// Configures the source secrets. /// The . public static IConfigurationBuilder Add(this IConfigurationBuilder builder, Action configureSource) where TSource : IConfigurationSource, new() { var source = new TSource(); configureSource?.Invoke(source); return builder.Add(source); } /// /// Shorthand for GetSection("ConnectionStrings")[name]. /// /// The configuration. /// The connection string key. /// public static string GetConnectionString(this IConfiguration configuration, string name) { return configuration?.GetSection("ConnectionStrings")?[name]; } /// /// Get the enumeration of key value pairs within the /// /// The to enumerate. /// An enumeration of key value pairs. public static IEnumerable> AsEnumerable(this IConfiguration configuration) => configuration.AsEnumerable(makePathsRelative: false); /// /// Get the enumeration of key value pairs within the /// /// The to enumerate. /// If true, the child keys returned will have the current configuration's Path trimmed from the front. /// An enumeration of key value pairs. public static IEnumerable> AsEnumerable(this IConfiguration configuration, bool makePathsRelative) { var stack = new Stack(); stack.Push(configuration); var rootSection = configuration as IConfigurationSection; var prefixLength = (makePathsRelative && rootSection != null) ? rootSection.Path.Length + 1 : 0; while (stack.Count > 0) { var config = stack.Pop(); // Don't include the sections value if we are removing paths, since it will be an empty key if (config is IConfigurationSection section && (!makePathsRelative || config != configuration)) { yield return new KeyValuePair(section.Path.Substring(prefixLength), section.Value); } foreach (var child in config.GetChildren()) { stack.Push(child); } } } /// /// Determines whether the section has a or has children /// public static bool Exists(this IConfigurationSection section) { if (section == null) { return false; } return section.Value != null || section.GetChildren().Any(); } } } ================================================ FILE: src/Config.Abstractions/ConfigurationPath.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; namespace Microsoft.Extensions.Configuration { /// /// Utility methods and constants for manipulating Configuration paths /// public static class ConfigurationPath { /// /// The delimiter ":" used to separate individual keys in a path. /// public static readonly string KeyDelimiter = ":"; /// /// Combines path segments into one path. /// /// The path segments to combine. /// The combined path. public static string Combine(params string[] pathSegments) { if (pathSegments == null) { throw new ArgumentNullException(nameof(pathSegments)); } return string.Join(KeyDelimiter, pathSegments); } /// /// Combines path segments into one path. /// /// The path segments to combine. /// The combined path. public static string Combine(IEnumerable pathSegments) { if (pathSegments == null) { throw new ArgumentNullException(nameof(pathSegments)); } return string.Join(KeyDelimiter, pathSegments); } /// /// Extracts the last path segment from the path. /// /// The path. /// The last path segment of the path. public static string GetSectionKey(string path) { if (string.IsNullOrEmpty(path)) { return path; } var lastDelimiterIndex = path.LastIndexOf(KeyDelimiter, StringComparison.OrdinalIgnoreCase); return lastDelimiterIndex == -1 ? path : path.Substring(lastDelimiterIndex + 1); } /// /// Extracts the path corresponding to the parent node for a given path. /// /// The path. /// The original path minus the last individual segment found in it. Null if the original path corresponds to a top level node. public static string GetParentPath(string path) { if (string.IsNullOrEmpty(path)) { return null; } var lastDelimiterIndex = path.LastIndexOf(KeyDelimiter, StringComparison.OrdinalIgnoreCase); return lastDelimiterIndex == -1 ? null : path.Substring(0, lastDelimiterIndex); } } } ================================================ FILE: src/Config.Abstractions/IConfiguration.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using Microsoft.Extensions.Primitives; namespace Microsoft.Extensions.Configuration { /// /// Represents a set of key/value application configuration properties. /// public interface IConfiguration { /// /// Gets or sets a configuration value. /// /// The configuration key. /// The configuration value. string this[string key] { get; set; } /// /// Gets a configuration sub-section with the specified key. /// /// The key of the configuration section. /// The . /// /// This method will never return null. If no matching sub-section is found with the specified key, /// an empty will be returned. /// IConfigurationSection GetSection(string key); /// /// Gets the immediate descendant configuration sub-sections. /// /// The configuration sub-sections. IEnumerable GetChildren(); /// /// Returns a that can be used to observe when this configuration is reloaded. /// /// A . IChangeToken GetReloadToken(); } } ================================================ FILE: src/Config.Abstractions/IConfigurationBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace Microsoft.Extensions.Configuration { /// /// Represents a type used to build application configuration. /// public interface IConfigurationBuilder { /// /// Gets a key/value collection that can be used to share data between the /// and the registered s. /// IDictionary Properties { get; } /// /// Gets the sources used to obtain configuration values /// IList Sources { get; } /// /// Adds a new configuration source. /// /// The configuration source to add. /// The same . IConfigurationBuilder Add(IConfigurationSource source); /// /// Builds an with keys and values from the set of sources registered in /// . /// /// An with keys and values from the registered sources. IConfigurationRoot Build(); } } ================================================ FILE: src/Config.Abstractions/IConfigurationProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using Microsoft.Extensions.Primitives; namespace Microsoft.Extensions.Configuration { /// /// Provides configuration key/values for an application. /// public interface IConfigurationProvider { /// /// Tries to get a configuration value for the specified key. /// /// The key. /// The value. /// True if a value for the specified key was found, otherwise false. bool TryGet(string key, out string value); /// /// Sets a configuration value for the specified key. /// /// The key. /// The value. void Set(string key, string value); /// /// Returns a change token if this provider supports change tracking, null otherwise. /// /// IChangeToken GetReloadToken(); /// /// Loads configuration values from the source represented by this . /// void Load(); /// /// Returns the immediate descendant configuration keys for a given parent path based on this /// 's data and the set of keys returned by all the preceding /// s. /// /// The child keys returned by the preceding providers for the same parent path. /// The parent path. /// The child keys. IEnumerable GetChildKeys(IEnumerable earlierKeys, string parentPath); } } ================================================ FILE: src/Config.Abstractions/IConfigurationRoot.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; namespace Microsoft.Extensions.Configuration { /// /// Represents the root of an hierarchy. /// public interface IConfigurationRoot : IConfiguration { /// /// Force the configuration values to be reloaded from the underlying s. /// void Reload(); /// /// The s for this configuration. /// IEnumerable Providers { get; } } } ================================================ FILE: src/Config.Abstractions/IConfigurationSection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Extensions.Configuration { /// /// Represents a section of application configuration values. /// public interface IConfigurationSection : IConfiguration { /// /// Gets the key this section occupies in its parent. /// string Key { get; } /// /// Gets the full path to this section within the . /// string Path { get; } /// /// Gets or sets the section value. /// string Value { get; set; } } } ================================================ FILE: src/Config.Abstractions/IConfigurationSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace Microsoft.Extensions.Configuration { /// /// Represents a source of configuration key/values for an application. /// public interface IConfigurationSource { /// /// Builds the for this source. /// /// The . /// An IConfigurationProvider Build(IConfigurationBuilder builder); } } ================================================ FILE: src/Config.Abstractions/baseline.netcore.json ================================================ { "AssemblyIdentity": "Microsoft.Extensions.Configuration.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.Configuration.ConfigurationExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Add", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "configureSource", "Type": "System.Action" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [ { "ParameterName": "TSource", "ParameterPosition": 0, "New": true, "BaseTypeOrInterfaces": [ "Microsoft.Extensions.Configuration.IConfigurationSource" ] } ] }, { "Kind": "Method", "Name": "GetConnectionString", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "name", "Type": "System.String" } ], "ReturnType": "System.String", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AsEnumerable", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" } ], "ReturnType": "System.Collections.Generic.IEnumerable>", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AsEnumerable", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "makePathsRelative", "Type": "System.Boolean" } ], "ReturnType": "System.Collections.Generic.IEnumerable>", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Exists", "Parameters": [ { "Name": "section", "Type": "Microsoft.Extensions.Configuration.IConfigurationSection" } ], "ReturnType": "System.Boolean", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.ConfigurationPath", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Combine", "Parameters": [ { "Name": "pathSegments", "Type": "System.String[]", "IsParams": true } ], "ReturnType": "System.String", "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Combine", "Parameters": [ { "Name": "pathSegments", "Type": "System.Collections.Generic.IEnumerable" } ], "ReturnType": "System.String", "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetSectionKey", "Parameters": [ { "Name": "path", "Type": "System.String" } ], "ReturnType": "System.String", "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetParentPath", "Parameters": [ { "Name": "path", "Type": "System.String" } ], "ReturnType": "System.String", "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Field", "Name": "KeyDelimiter", "Parameters": [], "ReturnType": "System.String", "Static": true, "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.IConfiguration", "Visibility": "Public", "Kind": "Interface", "Abstract": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "get_Item", "Parameters": [ { "Name": "key", "Type": "System.String" } ], "ReturnType": "System.String", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Item", "Parameters": [ { "Name": "key", "Type": "System.String" }, { "Name": "value", "Type": "System.String" } ], "ReturnType": "System.Void", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetSection", "Parameters": [ { "Name": "key", "Type": "System.String" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationSection", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetChildren", "Parameters": [], "ReturnType": "System.Collections.Generic.IEnumerable", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetReloadToken", "Parameters": [], "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Visibility": "Public", "Kind": "Interface", "Abstract": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "get_Properties", "Parameters": [], "ReturnType": "System.Collections.Generic.IDictionary", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Sources", "Parameters": [], "ReturnType": "System.Collections.Generic.IList", "GenericParameter": [] }, { "Kind": "Method", "Name": "Add", "Parameters": [ { "Name": "source", "Type": "Microsoft.Extensions.Configuration.IConfigurationSource" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "GenericParameter": [] }, { "Kind": "Method", "Name": "Build", "Parameters": [], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationRoot", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "Kind": "Interface", "Abstract": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "TryGet", "Parameters": [ { "Name": "key", "Type": "System.String" }, { "Name": "value", "Type": "System.String", "Direction": "Out" } ], "ReturnType": "System.Boolean", "GenericParameter": [] }, { "Kind": "Method", "Name": "Set", "Parameters": [ { "Name": "key", "Type": "System.String" }, { "Name": "value", "Type": "System.String" } ], "ReturnType": "System.Void", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetReloadToken", "Parameters": [], "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", "GenericParameter": [] }, { "Kind": "Method", "Name": "Load", "Parameters": [], "ReturnType": "System.Void", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetChildKeys", "Parameters": [ { "Name": "earlierKeys", "Type": "System.Collections.Generic.IEnumerable" }, { "Name": "parentPath", "Type": "System.String" } ], "ReturnType": "System.Collections.Generic.IEnumerable", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.IConfigurationRoot", "Visibility": "Public", "Kind": "Interface", "Abstract": true, "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfiguration" ], "Members": [ { "Kind": "Method", "Name": "Reload", "Parameters": [], "ReturnType": "System.Void", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Providers", "Parameters": [], "ReturnType": "System.Collections.Generic.IEnumerable", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.IConfigurationSection", "Visibility": "Public", "Kind": "Interface", "Abstract": true, "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfiguration" ], "Members": [ { "Kind": "Method", "Name": "get_Key", "Parameters": [], "ReturnType": "System.String", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Path", "Parameters": [], "ReturnType": "System.String", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Value", "Parameters": [], "ReturnType": "System.String", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Value", "Parameters": [ { "Name": "value", "Type": "System.String" } ], "ReturnType": "System.Void", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.IConfigurationSource", "Visibility": "Public", "Kind": "Interface", "Abstract": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Build", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationProvider", "GenericParameter": [] } ], "GenericParameters": [] } ] } ================================================ FILE: src/Config.AzureKeyVault/AzureKeyVaultConfigurationExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.Azure.KeyVault; using Microsoft.Azure.Services.AppAuthentication; using Microsoft.Extensions.Configuration.AzureKeyVault; using Microsoft.IdentityModel.Clients.ActiveDirectory; namespace Microsoft.Extensions.Configuration { /// /// Extension methods for registering with . /// public static class AzureKeyVaultConfigurationExtensions { /// /// Adds an that reads configuration values from the Azure KeyVault. /// /// The to add to. /// The Azure KeyVault uri. /// The application client id. /// The client secret to use for authentication. /// The . public static IConfigurationBuilder AddAzureKeyVault( this IConfigurationBuilder configurationBuilder, string vault, string clientId, string clientSecret) { return AddAzureKeyVault(configurationBuilder, vault, clientId, clientSecret, new DefaultKeyVaultSecretManager()); } /// /// Adds an that reads configuration values from the Azure KeyVault. /// /// The to add to. /// The Azure KeyVault uri. /// The application client id. /// The client secret to use for authentication. /// The instance used to control secret loading. /// The . public static IConfigurationBuilder AddAzureKeyVault( this IConfigurationBuilder configurationBuilder, string vault, string clientId, string clientSecret, IKeyVaultSecretManager manager) { if (clientId == null) { throw new ArgumentNullException(nameof(clientId)); } if (clientSecret == null) { throw new ArgumentNullException(nameof(clientSecret)); } KeyVaultClient.AuthenticationCallback callback = (authority, resource, scope) => GetTokenFromClientSecret(authority, resource, clientId, clientSecret); return configurationBuilder.AddAzureKeyVault(vault, new KeyVaultClient(callback), manager); } private static async Task GetTokenFromClientSecret(string authority, string resource, string clientId, string clientSecret) { var authContext = new AuthenticationContext(authority); var clientCred = new ClientCredential(clientId, clientSecret); var result = await authContext.AcquireTokenAsync(resource, clientCred); return result.AccessToken; } /// /// Adds an that reads configuration values from the Azure KeyVault. /// /// The to add to. /// Azure KeyVault uri. /// The application client id. /// The to use for authentication. /// The . public static IConfigurationBuilder AddAzureKeyVault( this IConfigurationBuilder configurationBuilder, string vault, string clientId, X509Certificate2 certificate) { return AddAzureKeyVault(configurationBuilder, vault, clientId, certificate, new DefaultKeyVaultSecretManager()); } /// /// Adds an that reads configuration values from the Azure KeyVault. /// /// The to add to. /// Azure KeyVault uri. /// The application client id. /// The to use for authentication. /// The instance used to control secret loading. /// The . public static IConfigurationBuilder AddAzureKeyVault( this IConfigurationBuilder configurationBuilder, string vault, string clientId, X509Certificate2 certificate, IKeyVaultSecretManager manager) { if (clientId == null) { throw new ArgumentNullException(nameof(clientId)); } if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } KeyVaultClient.AuthenticationCallback callback = (authority, resource, scope) => GetTokenFromClientCertificate(authority, resource, clientId, certificate); return configurationBuilder.AddAzureKeyVault(vault, new KeyVaultClient(callback), manager); } private static async Task GetTokenFromClientCertificate(string authority, string resource, string clientId, X509Certificate2 certificate) { var authContext = new AuthenticationContext(authority); var result = await authContext.AcquireTokenAsync(resource, new ClientAssertionCertificate(clientId, certificate)); return result.AccessToken; } /// /// Adds an that reads configuration values from the Azure KeyVault. /// /// The to add to. /// Azure KeyVault uri. /// The . public static IConfigurationBuilder AddAzureKeyVault( this IConfigurationBuilder configurationBuilder, string vault) { return AddAzureKeyVault(configurationBuilder, vault, new DefaultKeyVaultSecretManager()); } /// /// Adds an that reads configuration values from the Azure KeyVault. /// /// The to add to. /// Azure KeyVault uri. /// The instance used to control secret loading. /// The . public static IConfigurationBuilder AddAzureKeyVault( this IConfigurationBuilder configurationBuilder, string vault, IKeyVaultSecretManager manager) { var azureServiceTokenProvider = new AzureServiceTokenProvider(); var authenticationCallback = new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback); var keyVaultClient = new KeyVaultClient(authenticationCallback); return AddAzureKeyVault(configurationBuilder, vault, keyVaultClient, manager); } /// /// Adds an that reads configuration values from the Azure KeyVault. /// /// The to add to. /// Azure KeyVault uri. /// The to use for retrieving values. /// The instance used to control secret loading. /// The . public static IConfigurationBuilder AddAzureKeyVault( this IConfigurationBuilder configurationBuilder, string vault, KeyVaultClient client, IKeyVaultSecretManager manager) { if (configurationBuilder == null) { throw new ArgumentNullException(nameof(configurationBuilder)); } if (client == null) { throw new ArgumentNullException(nameof(client)); } if (vault == null) { throw new ArgumentNullException(nameof(vault)); } if (manager == null) { throw new ArgumentNullException(nameof(manager)); } configurationBuilder.Add(new AzureKeyVaultConfigurationSource() { Client = client, Vault = vault, Manager = manager }); return configurationBuilder; } } } ================================================ FILE: src/Config.AzureKeyVault/AzureKeyVaultConfigurationProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Azure.KeyVault; namespace Microsoft.Extensions.Configuration.AzureKeyVault { /// /// An AzureKeyVault based . /// internal class AzureKeyVaultConfigurationProvider : ConfigurationProvider { private readonly IKeyVaultClient _client; private readonly string _vault; private readonly IKeyVaultSecretManager _manager; /// /// Creates a new instance of . /// /// The to use for retrieving values. /// Azure KeyVault uri. /// public AzureKeyVaultConfigurationProvider(IKeyVaultClient client, string vault, IKeyVaultSecretManager manager) { if (client == null) { throw new ArgumentNullException(nameof(client)); } if (vault == null) { throw new ArgumentNullException(nameof(vault)); } if (manager == null) { throw new ArgumentNullException(nameof(manager)); } _client = client; _vault = vault; _manager = manager; } public override void Load() => LoadAsync().ConfigureAwait(false).GetAwaiter().GetResult(); private async Task LoadAsync() { var data = new Dictionary(StringComparer.OrdinalIgnoreCase); var secrets = await _client.GetSecretsAsync(_vault).ConfigureAwait(false); do { foreach (var secretItem in secrets) { if (!_manager.Load(secretItem) || (secretItem.Attributes?.Enabled != true)) { continue; } var value = await _client.GetSecretAsync(secretItem.Id).ConfigureAwait(false); var key = _manager.GetKey(value); data.Add(key, value.Value); } secrets = secrets.NextPageLink != null ? await _client.GetSecretsNextAsync(secrets.NextPageLink).ConfigureAwait(false) : null; } while (secrets != null); Data = data; } } } ================================================ FILE: src/Config.AzureKeyVault/AzureKeyVaultConfigurationSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Azure.KeyVault; namespace Microsoft.Extensions.Configuration.AzureKeyVault { /// /// Represents Azure KeyVault secrets as an . /// internal class AzureKeyVaultConfigurationSource : IConfigurationSource { /// /// Gets or sets the to use for retrieving values. /// public KeyVaultClient Client { get; set; } /// /// Gets or sets the vault uri. /// public string Vault { get; set; } /// /// Gets or sets the instance used to control secret loading. /// public IKeyVaultSecretManager Manager { get; set; } /// public IConfigurationProvider Build(IConfigurationBuilder builder) { return new AzureKeyVaultConfigurationProvider(new KeyVaultClientWrapper(Client), Vault, Manager); } } } ================================================ FILE: src/Config.AzureKeyVault/Config.AzureKeyVault.csproj ================================================  Microsoft.Extensions.Configuration.AzureKeyVault Microsoft.Extensions.Configuration.AzureKeyVault Azure KeyVault configuration provider implementation for Microsoft.Extensions.Configuration. netstandard2.0 $(PackageTags);azure;keyvault ================================================ FILE: src/Config.AzureKeyVault/DefaultKeyVaultSecretManager.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Azure.KeyVault; using Microsoft.Azure.KeyVault.Models; namespace Microsoft.Extensions.Configuration.AzureKeyVault { /// /// Default implementation of that loads all secrets /// and replaces '--' with ':" in key names. /// public class DefaultKeyVaultSecretManager : IKeyVaultSecretManager { /// public virtual string GetKey(SecretBundle secret) { return secret.SecretIdentifier.Name.Replace("--", ConfigurationPath.KeyDelimiter); } /// public virtual bool Load(SecretItem secret) { return true; } } } ================================================ FILE: src/Config.AzureKeyVault/IKeyVaultClient.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading.Tasks; using Microsoft.Azure.KeyVault; using Microsoft.Azure.KeyVault.Models; using Microsoft.Rest.Azure; namespace Microsoft.Extensions.Configuration.AzureKeyVault { /// /// Client class to perform cryptographic key operations and vault operations /// against the Key Vault service. /// Thread safety: This class is thread-safe. /// internal interface IKeyVaultClient { /// List secrets in the specified vault /// The URL for the vault containing the secrets. /// A response message containing a list of secrets in the vault along with a link to the next page of secrets Task> GetSecretsAsync(string vault); /// Gets a secret. /// The URL for the secret. /// A response message containing the secret Task GetSecretAsync(string secretIdentifier); /// List the next page of secrets /// nextLink value from a previous call to GetSecrets or GetSecretsNext /// A response message containing a list of secrets in the vault along with a link to the next page of secrets Task> GetSecretsNextAsync(string nextLink); } } ================================================ FILE: src/Config.AzureKeyVault/IKeyVaultSecretManager.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Azure.KeyVault.Models; namespace Microsoft.Extensions.Configuration.AzureKeyVault { /// /// The instance used to control secret loading. /// public interface IKeyVaultSecretManager { /// /// Checks if value should be retrieved. /// /// The instance. /// true is secrets value should be loaded, otherwise false. bool Load(SecretItem secret); /// /// Maps secret to a configuration key. /// /// The instance. /// Configuration key name to store secret value. string GetKey(SecretBundle secret); } } ================================================ FILE: src/Config.AzureKeyVault/KeyVaultClientWrapper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading.Tasks; using Microsoft.Azure.KeyVault; using Microsoft.Azure.KeyVault.Models; using Microsoft.Rest.Azure; namespace Microsoft.Extensions.Configuration.AzureKeyVault { /// internal class KeyVaultClientWrapper : IKeyVaultClient { private readonly KeyVaultClient _keyVaultClientImplementation; /// /// Creates a new instance of . /// /// The instance to wrap. public KeyVaultClientWrapper(KeyVaultClient keyVaultClientImplementation) { _keyVaultClientImplementation = keyVaultClientImplementation; } /// public Task> GetSecretsAsync(string vault) { return _keyVaultClientImplementation.GetSecretsAsync(vault); } /// public Task GetSecretAsync(string secretIdentifier) { return _keyVaultClientImplementation.GetSecretAsync(secretIdentifier); } /// public Task> GetSecretsNextAsync(string nextLink) { return _keyVaultClientImplementation.GetSecretsNextAsync(nextLink); } } } ================================================ FILE: src/Config.AzureKeyVault/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.Extensions.Configuration.AzureKeyVault.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] ================================================ FILE: src/Config.AzureKeyVault/baseline.netcore.json ================================================ { "AssemblyIdentity": "Microsoft.Extensions.Configuration.AzureKeyVault, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.Configuration.AzureKeyVaultConfigurationExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "AddAzureKeyVault", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "vault", "Type": "System.String" }, { "Name": "clientId", "Type": "System.String" }, { "Name": "clientSecret", "Type": "System.String" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddAzureKeyVault", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "vault", "Type": "System.String" }, { "Name": "clientId", "Type": "System.String" }, { "Name": "clientSecret", "Type": "System.String" }, { "Name": "manager", "Type": "Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddAzureKeyVault", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "vault", "Type": "System.String" }, { "Name": "clientId", "Type": "System.String" }, { "Name": "certificate", "Type": "System.Security.Cryptography.X509Certificates.X509Certificate2" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddAzureKeyVault", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "vault", "Type": "System.String" }, { "Name": "clientId", "Type": "System.String" }, { "Name": "certificate", "Type": "System.Security.Cryptography.X509Certificates.X509Certificate2" }, { "Name": "manager", "Type": "Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddAzureKeyVault", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "vault", "Type": "System.String" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddAzureKeyVault", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "vault", "Type": "System.String" }, { "Name": "manager", "Type": "Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddAzureKeyVault", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "vault", "Type": "System.String" }, { "Name": "client", "Type": "Microsoft.Azure.KeyVault.KeyVaultClient" }, { "Name": "manager", "Type": "Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.AzureKeyVault.DefaultKeyVaultSecretManager", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager" ], "Members": [ { "Kind": "Method", "Name": "GetKey", "Parameters": [ { "Name": "secret", "Type": "Microsoft.Azure.KeyVault.Models.SecretBundle" } ], "ReturnType": "System.String", "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Load", "Parameters": [ { "Name": "secret", "Type": "Microsoft.Azure.KeyVault.Models.SecretItem" } ], "ReturnType": "System.Boolean", "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager", "Visibility": "Public", "Kind": "Interface", "Abstract": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Load", "Parameters": [ { "Name": "secret", "Type": "Microsoft.Azure.KeyVault.Models.SecretItem" } ], "ReturnType": "System.Boolean", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetKey", "Parameters": [ { "Name": "secret", "Type": "Microsoft.Azure.KeyVault.Models.SecretBundle" } ], "ReturnType": "System.String", "GenericParameter": [] } ], "GenericParameters": [] } ] } ================================================ FILE: src/Config.Binder/BinderOptions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Extensions.Configuration { /// /// Options class used by the . /// public class BinderOptions { /// /// When false (the default), the binder will only attempt to set public properties. /// If true, the binder will attempt to set all non read-only properties. /// public bool BindNonPublicProperties { get; set; } } } ================================================ FILE: src/Config.Binder/Config.Binder.csproj ================================================  Microsoft.Extensions.Configuration.Binder Microsoft.Extensions.Configuration.Binder Functionality to bind an object to data in configuration providers for Microsoft.Extensions.Configuration. netstandard2.0 ================================================ FILE: src/Config.Binder/ConfigurationBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using Microsoft.Extensions.Configuration.Binder; namespace Microsoft.Extensions.Configuration { /// /// Static helper class that allows binding strongly typed objects to configuration values. /// public static class ConfigurationBinder { /// /// Attempts to bind the configuration instance to a new instance of type T. /// If this configuration section has a value, that will be used. /// Otherwise binding by matching property names against configuration keys recursively. /// /// The type of the new instance to bind. /// The configuration instance to bind. /// The new instance of T if successful, default(T) otherwise. public static T Get(this IConfiguration configuration) => configuration.Get(_ => { }); /// /// Attempts to bind the configuration instance to a new instance of type T. /// If this configuration section has a value, that will be used. /// Otherwise binding by matching property names against configuration keys recursively. /// /// The type of the new instance to bind. /// The configuration instance to bind. /// Configures the binder options. /// The new instance of T if successful, default(T) otherwise. public static T Get(this IConfiguration configuration, Action configureOptions) { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } var result = configuration.Get(typeof(T), configureOptions); if (result == null) { return default(T); } return (T)result; } /// /// Attempts to bind the configuration instance to a new instance of type T. /// If this configuration section has a value, that will be used. /// Otherwise binding by matching property names against configuration keys recursively. /// /// The configuration instance to bind. /// The type of the new instance to bind. /// The new instance if successful, null otherwise. public static object Get(this IConfiguration configuration, Type type) => configuration.Get(type, _ => { }); /// /// Attempts to bind the configuration instance to a new instance of type T. /// If this configuration section has a value, that will be used. /// Otherwise binding by matching property names against configuration keys recursively. /// /// The configuration instance to bind. /// The type of the new instance to bind. /// Configures the binder options. /// The new instance if successful, null otherwise. public static object Get(this IConfiguration configuration, Type type, Action configureOptions) { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } var options = new BinderOptions(); configureOptions?.Invoke(options); return BindInstance(type, instance: null, config: configuration, options: options); } /// /// Attempts to bind the given object instance to the configuration section specified by the key by matching property names against configuration keys recursively. /// /// The configuration instance to bind. /// The key of the configuration section to bind. /// The object to bind. public static void Bind(this IConfiguration configuration, string key, object instance) => configuration.GetSection(key).Bind(instance); /// /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. /// /// The configuration instance to bind. /// The object to bind. public static void Bind(this IConfiguration configuration, object instance) => configuration.Bind(instance, o => { }); /// /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. /// /// The configuration instance to bind. /// The object to bind. /// Configures the binder options. public static void Bind(this IConfiguration configuration, object instance, Action configureOptions) { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } if (instance != null) { var options = new BinderOptions(); configureOptions?.Invoke(options); BindInstance(instance.GetType(), instance, configuration, options); } } /// /// Extracts the value with the specified key and converts it to type T. /// /// The type to convert the value to. /// The configuration. /// The key of the configuration section's value to convert. /// The converted value. public static T GetValue(this IConfiguration configuration, string key) { return GetValue(configuration, key, default(T)); } /// /// Extracts the value with the specified key and converts it to type T. /// /// The type to convert the value to. /// The configuration. /// The key of the configuration section's value to convert. /// The default value to use if no value is found. /// The converted value. public static T GetValue(this IConfiguration configuration, string key, T defaultValue) { return (T)GetValue(configuration, typeof(T), key, defaultValue); } /// /// Extracts the value with the specified key and converts it to the specified type. /// /// The configuration. /// The type to convert the value to. /// The key of the configuration section's value to convert. /// The converted value. public static object GetValue(this IConfiguration configuration, Type type, string key) { return GetValue(configuration, type, key, defaultValue: null); } /// /// Extracts the value with the specified key and converts it to the specified type. /// /// The configuration. /// The type to convert the value to. /// The key of the configuration section's value to convert. /// The default value to use if no value is found. /// The converted value. public static object GetValue(this IConfiguration configuration, Type type, string key, object defaultValue) { var value = configuration.GetSection(key).Value; if (value != null) { return ConvertValue(type, value); } return defaultValue; } private static void BindNonScalar(this IConfiguration configuration, object instance, BinderOptions options) { if (instance != null) { foreach (var property in GetAllProperties(instance.GetType().GetTypeInfo())) { BindProperty(property, instance, configuration, options); } } } private static void BindProperty(PropertyInfo property, object instance, IConfiguration config, BinderOptions options) { // We don't support set only, non public, or indexer properties if (property.GetMethod == null || (!options.BindNonPublicProperties && !property.GetMethod.IsPublic) || property.GetMethod.GetParameters().Length > 0) { return; } var propertyValue = property.GetValue(instance); var hasSetter = property.SetMethod != null && (property.SetMethod.IsPublic || options.BindNonPublicProperties); if (propertyValue == null && !hasSetter) { // Property doesn't have a value and we cannot set it so there is no // point in going further down the graph return; } propertyValue = BindInstance(property.PropertyType, propertyValue, config.GetSection(property.Name), options); if (propertyValue != null && hasSetter) { property.SetValue(instance, propertyValue); } } private static object BindToCollection(TypeInfo typeInfo, IConfiguration config, BinderOptions options) { var type = typeof(List<>).MakeGenericType(typeInfo.GenericTypeArguments[0]); var instance = Activator.CreateInstance(type); BindCollection(instance, type, config, options); return instance; } // Try to create an array/dictionary instance to back various collection interfaces private static object AttemptBindToCollectionInterfaces(Type type, IConfiguration config, BinderOptions options) { var typeInfo = type.GetTypeInfo(); if (!typeInfo.IsInterface) { return null; } var collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyList<>), type); if (collectionInterface != null) { // IEnumerable is guaranteed to have exactly one parameter return BindToCollection(typeInfo, config, options); } collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyDictionary<,>), type); if (collectionInterface != null) { var dictionaryType = typeof(Dictionary<,>).MakeGenericType(typeInfo.GenericTypeArguments[0], typeInfo.GenericTypeArguments[1]); var instance = Activator.CreateInstance(dictionaryType); BindDictionary(instance, dictionaryType, config, options); return instance; } collectionInterface = FindOpenGenericInterface(typeof(IDictionary<,>), type); if (collectionInterface != null) { var instance = Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(typeInfo.GenericTypeArguments[0], typeInfo.GenericTypeArguments[1])); BindDictionary(instance, collectionInterface, config, options); return instance; } collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyCollection<>), type); if (collectionInterface != null) { // IReadOnlyCollection is guaranteed to have exactly one parameter return BindToCollection(typeInfo, config, options); } collectionInterface = FindOpenGenericInterface(typeof(ICollection<>), type); if (collectionInterface != null) { // ICollection is guaranteed to have exactly one parameter return BindToCollection(typeInfo, config, options); } collectionInterface = FindOpenGenericInterface(typeof(IEnumerable<>), type); if (collectionInterface != null) { // IEnumerable is guaranteed to have exactly one parameter return BindToCollection(typeInfo, config, options); } return null; } private static object BindInstance(Type type, object instance, IConfiguration config, BinderOptions options) { // if binding IConfigurationSection, break early if (type == typeof(IConfigurationSection)) { return config; } var section = config as IConfigurationSection; var configValue = section?.Value; object convertedValue; Exception error; if (configValue != null && TryConvertValue(type, configValue, out convertedValue, out error)) { if (error != null) { throw error; } // Leaf nodes are always reinitialized return convertedValue; } if (config != null && config.GetChildren().Any()) { // If we don't have an instance, try to create one if (instance == null) { // We are alrady done if binding to a new collection instance worked instance = AttemptBindToCollectionInterfaces(type, config, options); if (instance != null) { return instance; } instance = CreateInstance(type); } // See if its a Dictionary var collectionInterface = FindOpenGenericInterface(typeof(IDictionary<,>), type); if (collectionInterface != null) { BindDictionary(instance, collectionInterface, config, options); } else if (type.IsArray) { instance = BindArray((Array)instance, config, options); } else { // See if its an ICollection collectionInterface = FindOpenGenericInterface(typeof(ICollection<>), type); if (collectionInterface != null) { BindCollection(instance, collectionInterface, config, options); } // Something else else { BindNonScalar(config, instance, options); } } } return instance; } private static object CreateInstance(Type type) { var typeInfo = type.GetTypeInfo(); if (typeInfo.IsInterface || typeInfo.IsAbstract) { throw new InvalidOperationException(Resources.FormatError_CannotActivateAbstractOrInterface(type)); } if (type.IsArray) { if (typeInfo.GetArrayRank() > 1) { throw new InvalidOperationException(Resources.FormatError_UnsupportedMultidimensionalArray(type)); } return Array.CreateInstance(typeInfo.GetElementType(), 0); } var hasDefaultConstructor = typeInfo.DeclaredConstructors.Any(ctor => ctor.IsPublic && ctor.GetParameters().Length == 0); if (!hasDefaultConstructor) { throw new InvalidOperationException(Resources.FormatError_MissingParameterlessConstructor(type)); } try { return Activator.CreateInstance(type); } catch (Exception ex) { throw new InvalidOperationException(Resources.FormatError_FailedToActivate(type), ex); } } private static void BindDictionary(object dictionary, Type dictionaryType, IConfiguration config, BinderOptions options) { var typeInfo = dictionaryType.GetTypeInfo(); // IDictionary is guaranteed to have exactly two parameters var keyType = typeInfo.GenericTypeArguments[0]; var valueType = typeInfo.GenericTypeArguments[1]; var keyTypeIsEnum = keyType.GetTypeInfo().IsEnum; if (keyType != typeof(string) && !keyTypeIsEnum) { // We only support string and enum keys return; } var setter = typeInfo.GetDeclaredProperty("Item"); foreach (var child in config.GetChildren()) { var item = BindInstance( type: valueType, instance: null, config: child, options: options); if (item != null) { if (keyType == typeof(string)) { var key = child.Key; setter.SetValue(dictionary, item, new object[] { key }); } else if (keyTypeIsEnum) { var key = Convert.ToInt32(Enum.Parse(keyType, child.Key)); setter.SetValue(dictionary, item, new object[] { key }); } } } } private static void BindCollection(object collection, Type collectionType, IConfiguration config, BinderOptions options) { var typeInfo = collectionType.GetTypeInfo(); // ICollection is guaranteed to have exactly one parameter var itemType = typeInfo.GenericTypeArguments[0]; var addMethod = typeInfo.GetDeclaredMethod("Add"); foreach (var section in config.GetChildren()) { try { var item = BindInstance( type: itemType, instance: null, config: section, options: options); if (item != null) { addMethod.Invoke(collection, new[] { item }); } } catch { } } } private static Array BindArray(Array source, IConfiguration config, BinderOptions options) { var children = config.GetChildren().ToArray(); var arrayLength = source.Length; var elementType = source.GetType().GetElementType(); var newArray = Array.CreateInstance(elementType, arrayLength + children.Length); // binding to array has to preserve already initialized arrays with values if (arrayLength > 0) { Array.Copy(source, newArray, arrayLength); } for (int i = 0; i < children.Length; i++) { try { var item = BindInstance( type: elementType, instance: null, config: children[i], options: options); if (item != null) { newArray.SetValue(item, arrayLength + i); } } catch { } } return newArray; } private static bool TryConvertValue(Type type, string value, out object result, out Exception error) { error = null; result = null; if (type == typeof(object)) { result = value; return true; } if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { if (string.IsNullOrEmpty(value)) { return true; } return TryConvertValue(Nullable.GetUnderlyingType(type), value, out result, out error); } var converter = TypeDescriptor.GetConverter(type); if (converter.CanConvertFrom(typeof(string))) { try { result = converter.ConvertFromInvariantString(value); } catch (Exception ex) { error = new InvalidOperationException(Resources.FormatError_FailedBinding(value, type), ex); } return true; } return false; } private static object ConvertValue(Type type, string value) { object result; Exception error; TryConvertValue(type, value, out result, out error); if (error != null) { throw error; } return result; } private static Type FindOpenGenericInterface(Type expected, Type actual) { var actualTypeInfo = actual.GetTypeInfo(); if(actualTypeInfo.IsGenericType && actual.GetGenericTypeDefinition() == expected) { return actual; } var interfaces = actualTypeInfo.ImplementedInterfaces; foreach (var interfaceType in interfaces) { if (interfaceType.GetTypeInfo().IsGenericType && interfaceType.GetGenericTypeDefinition() == expected) { return interfaceType; } } return null; } private static IEnumerable GetAllProperties(TypeInfo type) { var allProperties = new List(); do { allProperties.AddRange(type.DeclaredProperties); type = type.BaseType.GetTypeInfo(); } while (type != typeof(object).GetTypeInfo()); return allProperties; } } } ================================================ FILE: src/Config.Binder/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.Extensions.Configuration.Binder.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] ================================================ FILE: src/Config.Binder/Properties/Resources.Designer.cs ================================================ // namespace Microsoft.Extensions.Configuration.Binder { using System.Globalization; using System.Reflection; using System.Resources; internal static class Resources { private static readonly ResourceManager _resourceManager = new ResourceManager("Microsoft.Extensions.Configuration.Binder.Resources", typeof(Resources).GetTypeInfo().Assembly); /// /// Cannot create instance of type '{0}' because it is either abstract or an interface. /// internal static string Error_CannotActivateAbstractOrInterface { get => GetString("Error_CannotActivateAbstractOrInterface"); } /// /// Cannot create instance of type '{0}' because it is either abstract or an interface. /// internal static string FormatError_CannotActivateAbstractOrInterface(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_CannotActivateAbstractOrInterface"), p0); /// /// Failed to convert '{0}' to type '{1}'. /// internal static string Error_FailedBinding { get => GetString("Error_FailedBinding"); } /// /// Failed to convert '{0}' to type '{1}'. /// internal static string FormatError_FailedBinding(object p0, object p1) => string.Format(CultureInfo.CurrentCulture, GetString("Error_FailedBinding"), p0, p1); /// /// Failed to create instance of type '{0}'. /// internal static string Error_FailedToActivate { get => GetString("Error_FailedToActivate"); } /// /// Failed to create instance of type '{0}'. /// internal static string FormatError_FailedToActivate(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_FailedToActivate"), p0); /// /// Cannot create instance of type '{0}' because it is missing a public parameterless constructor. /// internal static string Error_MissingParameterlessConstructor { get => GetString("Error_MissingParameterlessConstructor"); } /// /// Cannot create instance of type '{0}' because it is missing a public parameterless constructor. /// internal static string FormatError_MissingParameterlessConstructor(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_MissingParameterlessConstructor"), p0); /// /// Cannot create instance of type '{0}' because multidimensional arrays are not supported. /// internal static string Error_UnsupportedMultidimensionalArray { get => GetString("Error_UnsupportedMultidimensionalArray"); } /// /// Cannot create instance of type '{0}' because multidimensional arrays are not supported. /// internal static string FormatError_UnsupportedMultidimensionalArray(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_UnsupportedMultidimensionalArray"), p0); private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); System.Diagnostics.Debug.Assert(value != null); if (formatterNames != null) { for (var i = 0; i < formatterNames.Length; i++) { value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); } } return value; } } } ================================================ FILE: src/Config.Binder/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Cannot create instance of type '{0}' because it is either abstract or an interface. Failed to convert '{0}' to type '{1}'. Failed to create instance of type '{0}'. Cannot create instance of type '{0}' because it is missing a public parameterless constructor. Cannot create instance of type '{0}' because multidimensional arrays are not supported. ================================================ FILE: src/Config.Binder/baseline.netcore.json ================================================ { "AssemblyIdentity": "Microsoft.Extensions.Configuration.Binder, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.Configuration.BinderOptions", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "get_BindNonPublicProperties", "Parameters": [], "ReturnType": "System.Boolean", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_BindNonPublicProperties", "Parameters": [ { "Name": "value", "Type": "System.Boolean" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.ConfigurationBinder", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Get", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" } ], "ReturnType": "T0", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [ { "ParameterName": "T", "ParameterPosition": 0, "BaseTypeOrInterfaces": [] } ] }, { "Kind": "Method", "Name": "Get", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "configureOptions", "Type": "System.Action" } ], "ReturnType": "T0", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [ { "ParameterName": "T", "ParameterPosition": 0, "BaseTypeOrInterfaces": [] } ] }, { "Kind": "Method", "Name": "Get", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "type", "Type": "System.Type" } ], "ReturnType": "System.Object", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Get", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "type", "Type": "System.Type" }, { "Name": "configureOptions", "Type": "System.Action" } ], "ReturnType": "System.Object", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Bind", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "key", "Type": "System.String" }, { "Name": "instance", "Type": "System.Object" } ], "ReturnType": "System.Void", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Bind", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "instance", "Type": "System.Object" } ], "ReturnType": "System.Void", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Bind", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "instance", "Type": "System.Object" }, { "Name": "configureOptions", "Type": "System.Action" } ], "ReturnType": "System.Void", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetValue", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "key", "Type": "System.String" } ], "ReturnType": "T0", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [ { "ParameterName": "T", "ParameterPosition": 0, "BaseTypeOrInterfaces": [] } ] }, { "Kind": "Method", "Name": "GetValue", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "key", "Type": "System.String" }, { "Name": "defaultValue", "Type": "T0" } ], "ReturnType": "T0", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [ { "ParameterName": "T", "ParameterPosition": 0, "BaseTypeOrInterfaces": [] } ] }, { "Kind": "Method", "Name": "GetValue", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "type", "Type": "System.Type" }, { "Name": "key", "Type": "System.String" } ], "ReturnType": "System.Object", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetValue", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfiguration" }, { "Name": "type", "Type": "System.Type" }, { "Name": "key", "Type": "System.String" }, { "Name": "defaultValue", "Type": "System.Object" } ], "ReturnType": "System.Object", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] } ] } ================================================ FILE: src/Config.CommandLine/CommandLineConfigurationExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Microsoft.Extensions.Configuration.CommandLine; namespace Microsoft.Extensions.Configuration { /// /// Extension methods for registering with . /// public static class CommandLineConfigurationExtensions { /// /// Adds an that reads configuration values from the command line. /// /// The to add to. /// The command line args. /// The . public static IConfigurationBuilder AddCommandLine(this IConfigurationBuilder configurationBuilder, string[] args) { return configurationBuilder.AddCommandLine(args, switchMappings: null); } /// /// Adds an that reads configuration values from the command line using the specified switch mappings. /// /// The to add to. /// The command line args. /// The switch mappings. /// The . public static IConfigurationBuilder AddCommandLine( this IConfigurationBuilder configurationBuilder, string[] args, IDictionary switchMappings) { configurationBuilder.Add(new CommandLineConfigurationSource { Args = args, SwitchMappings = switchMappings }); return configurationBuilder; } /// /// Adds an that reads configuration values from the command line. /// /// The to add to. /// Configures the source. /// The . public static IConfigurationBuilder AddCommandLine(this IConfigurationBuilder builder, Action configureSource) => builder.Add(configureSource); } } ================================================ FILE: src/Config.CommandLine/CommandLineConfigurationProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; namespace Microsoft.Extensions.Configuration.CommandLine { /// /// A command line based . /// public class CommandLineConfigurationProvider : ConfigurationProvider { private readonly Dictionary _switchMappings; /// /// Initializes a new instance. /// /// The command line args. /// The switch mappings. public CommandLineConfigurationProvider(IEnumerable args, IDictionary switchMappings = null) { Args = args ?? throw new ArgumentNullException(nameof(args)); if (switchMappings != null) { _switchMappings = GetValidatedSwitchMappingsCopy(switchMappings); } } /// /// The command line arguments. /// protected IEnumerable Args { get; private set; } /// /// Loads the configuration data from the command line args. /// public override void Load() { var data = new Dictionary(StringComparer.OrdinalIgnoreCase); string key, value; using (var enumerator = Args.GetEnumerator()) { while (enumerator.MoveNext()) { var currentArg = enumerator.Current; var keyStartIndex = 0; if (currentArg.StartsWith("--")) { keyStartIndex = 2; } else if (currentArg.StartsWith("-")) { keyStartIndex = 1; } else if (currentArg.StartsWith("/")) { // "/SomeSwitch" is equivalent to "--SomeSwitch" when interpreting switch mappings // So we do a conversion to simplify later processing currentArg = string.Format("--{0}", currentArg.Substring(1)); keyStartIndex = 2; } var separator = currentArg.IndexOf('='); if (separator < 0) { // If there is neither equal sign nor prefix in current arugment, it is an invalid format if (keyStartIndex == 0) { // Ignore invalid formats continue; } // If the switch is a key in given switch mappings, interpret it if (_switchMappings != null && _switchMappings.ContainsKey(currentArg)) { key = _switchMappings[currentArg]; } // If the switch starts with a single "-" and it isn't in given mappings , it is an invalid usage so ignore it else if (keyStartIndex == 1) { continue; } // Otherwise, use the switch name directly as a key else { key = currentArg.Substring(keyStartIndex); } var previousKey = enumerator.Current; if (!enumerator.MoveNext()) { // ignore missing values continue; } value = enumerator.Current; } else { var keySegment = currentArg.Substring(0, separator); // If the switch is a key in given switch mappings, interpret it if (_switchMappings != null && _switchMappings.ContainsKey(keySegment)) { key = _switchMappings[keySegment]; } // If the switch starts with a single "-" and it isn't in given mappings , it is an invalid usage else if (keyStartIndex == 1) { throw new FormatException(Resources.FormatError_ShortSwitchNotDefined(currentArg)); } // Otherwise, use the switch name directly as a key else { key = currentArg.Substring(keyStartIndex, separator - keyStartIndex); } value = currentArg.Substring(separator + 1); } // Override value when key is duplicated. So we always have the last argument win. data[key] = value; } } Data = data; } private Dictionary GetValidatedSwitchMappingsCopy(IDictionary switchMappings) { // The dictionary passed in might be constructed with a case-sensitive comparer // However, the keys in configuration providers are all case-insensitive // So we check whether the given switch mappings contain duplicated keys with case-insensitive comparer var switchMappingsCopy = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var mapping in switchMappings) { // Only keys start with "--" or "-" are acceptable if (!mapping.Key.StartsWith("-") && !mapping.Key.StartsWith("--")) { throw new ArgumentException( Resources.FormatError_InvalidSwitchMapping(mapping.Key), nameof(switchMappings)); } if (switchMappingsCopy.ContainsKey(mapping.Key)) { throw new ArgumentException( Resources.FormatError_DuplicatedKeyInSwitchMappings(mapping.Key), nameof(switchMappings)); } switchMappingsCopy.Add(mapping.Key, mapping.Value); } return switchMappingsCopy; } } } ================================================ FILE: src/Config.CommandLine/CommandLineConfigurationSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace Microsoft.Extensions.Configuration.CommandLine { /// /// Represents command line arguments as an . /// public class CommandLineConfigurationSource : IConfigurationSource { /// /// Gets or sets the switch mappings. /// public IDictionary SwitchMappings { get; set; } /// /// Gets or sets the command line args. /// public IEnumerable Args { get; set; } /// /// Builds the for this source. /// /// The . /// A public IConfigurationProvider Build(IConfigurationBuilder builder) { return new CommandLineConfigurationProvider(Args, SwitchMappings); } } } ================================================ FILE: src/Config.CommandLine/Config.CommandLine.csproj ================================================  Microsoft.Extensions.Configuration.CommandLine Microsoft.Extensions.Configuration.CommandLine Command line configuration provider implementation for Microsoft.Extensions.Configuration. netstandard2.0 $(PackageTags);commandline ================================================ FILE: src/Config.CommandLine/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.Extensions.Configuration.CommandLine.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] ================================================ FILE: src/Config.CommandLine/Properties/Resources.Designer.cs ================================================ // namespace Microsoft.Extensions.Configuration.CommandLine { using System.Globalization; using System.Reflection; using System.Resources; internal static class Resources { private static readonly ResourceManager _resourceManager = new ResourceManager("Microsoft.Extensions.Configuration.CommandLine.Resources", typeof(Resources).GetTypeInfo().Assembly); /// /// Keys in switch mappings are case-insensitive. A duplicated key '{0}' was found. /// internal static string Error_DuplicatedKeyInSwitchMappings { get => GetString("Error_DuplicatedKeyInSwitchMappings"); } /// /// Keys in switch mappings are case-insensitive. A duplicated key '{0}' was found. /// internal static string FormatError_DuplicatedKeyInSwitchMappings(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_DuplicatedKeyInSwitchMappings"), p0); /// /// The switch mappings contain an invalid switch '{0}'. /// internal static string Error_InvalidSwitchMapping { get => GetString("Error_InvalidSwitchMapping"); } /// /// The switch mappings contain an invalid switch '{0}'. /// internal static string FormatError_InvalidSwitchMapping(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_InvalidSwitchMapping"), p0); /// /// The short switch '{0}' is not defined in the switch mappings. /// internal static string Error_ShortSwitchNotDefined { get => GetString("Error_ShortSwitchNotDefined"); } /// /// The short switch '{0}' is not defined in the switch mappings. /// internal static string FormatError_ShortSwitchNotDefined(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_ShortSwitchNotDefined"), p0); private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); System.Diagnostics.Debug.Assert(value != null); if (formatterNames != null) { for (var i = 0; i < formatterNames.Length; i++) { value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); } } return value; } } } ================================================ FILE: src/Config.CommandLine/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Keys in switch mappings are case-insensitive. A duplicated key '{0}' was found. The switch mappings contain an invalid switch '{0}'. The short switch '{0}' is not defined in the switch mappings. ================================================ FILE: src/Config.CommandLine/baseline.netcore.json ================================================ { "AssemblyIdentity": "Microsoft.Extensions.Configuration.CommandLine, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.Configuration.CommandLineConfigurationExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "AddCommandLine", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "args", "Type": "System.String[]" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddCommandLine", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "args", "Type": "System.String[]" }, { "Name": "switchMappings", "Type": "System.Collections.Generic.IDictionary" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddCommandLine", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "configureSource", "Type": "System.Action" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.CommandLine.CommandLineConfigurationProvider", "Visibility": "Public", "Kind": "Class", "BaseType": "Microsoft.Extensions.Configuration.ConfigurationProvider", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Load", "Parameters": [], "ReturnType": "System.Void", "Virtual": true, "Override": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Args", "Parameters": [], "ReturnType": "System.Collections.Generic.IEnumerable", "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { "Name": "args", "Type": "System.Collections.Generic.IEnumerable" }, { "Name": "switchMappings", "Type": "System.Collections.Generic.IDictionary", "DefaultValue": "null" } ], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.CommandLine.CommandLineConfigurationSource", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfigurationSource" ], "Members": [ { "Kind": "Method", "Name": "get_SwitchMappings", "Parameters": [], "ReturnType": "System.Collections.Generic.IDictionary", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_SwitchMappings", "Parameters": [ { "Name": "value", "Type": "System.Collections.Generic.IDictionary" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Args", "Parameters": [], "ReturnType": "System.Collections.Generic.IEnumerable", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Args", "Parameters": [ { "Name": "value", "Type": "System.Collections.Generic.IEnumerable" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Build", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSource", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] } ] } ================================================ FILE: src/Config.EnvironmentVariables/Config.EnvironmentVariables.csproj ================================================  Microsoft.Extensions.Configuration.EnvironmentVariables Microsoft.Extensions.Configuration.EnvironmentVariables Environment variables configuration provider implementation for Microsoft.Extensions.Configuration. netstandard2.0 $(PackageTags);envvar;environmentvariable ================================================ FILE: src/Config.EnvironmentVariables/EnvironmentVariablesConfigurationProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace Microsoft.Extensions.Configuration.EnvironmentVariables { /// /// An environment variable based . /// public class EnvironmentVariablesConfigurationProvider : ConfigurationProvider { private const string MySqlServerPrefix = "MYSQLCONNSTR_"; private const string SqlAzureServerPrefix = "SQLAZURECONNSTR_"; private const string SqlServerPrefix = "SQLCONNSTR_"; private const string CustomPrefix = "CUSTOMCONNSTR_"; private const string ConnStrKeyFormat = "ConnectionStrings:{0}"; private const string ProviderKeyFormat = "ConnectionStrings:{0}_ProviderName"; private readonly string _prefix; /// /// Initializes a new instance. /// public EnvironmentVariablesConfigurationProvider() : this(string.Empty) { } /// /// Initializes a new instance with the specified prefix. /// /// A prefix used to filter the environment variables. public EnvironmentVariablesConfigurationProvider(string prefix) { _prefix = prefix ?? string.Empty; } /// /// Loads the environment variables. /// public override void Load() { Load(Environment.GetEnvironmentVariables()); } internal void Load(IDictionary envVariables) { Data = new Dictionary(StringComparer.OrdinalIgnoreCase); var filteredEnvVariables = envVariables .Cast() .SelectMany(AzureEnvToAppEnv) .Where(entry => ((string)entry.Key).StartsWith(_prefix, StringComparison.OrdinalIgnoreCase)); foreach (var envVariable in filteredEnvVariables) { var key = ((string)envVariable.Key).Substring(_prefix.Length); Data[key] = (string)envVariable.Value; } } private static string NormalizeKey(string key) { return key.Replace("__", ConfigurationPath.KeyDelimiter); } private static IEnumerable AzureEnvToAppEnv(DictionaryEntry entry) { var key = (string)entry.Key; var prefix = string.Empty; var provider = string.Empty; if (key.StartsWith(MySqlServerPrefix, StringComparison.OrdinalIgnoreCase)) { prefix = MySqlServerPrefix; provider = "MySql.Data.MySqlClient"; } else if (key.StartsWith(SqlAzureServerPrefix, StringComparison.OrdinalIgnoreCase)) { prefix = SqlAzureServerPrefix; provider = "System.Data.SqlClient"; } else if (key.StartsWith(SqlServerPrefix, StringComparison.OrdinalIgnoreCase)) { prefix = SqlServerPrefix; provider = "System.Data.SqlClient"; } else if (key.StartsWith(CustomPrefix, StringComparison.OrdinalIgnoreCase)) { prefix = CustomPrefix; } else { entry.Key = NormalizeKey(key); yield return entry; yield break; } // Return the key-value pair for connection string yield return new DictionaryEntry( string.Format(ConnStrKeyFormat, NormalizeKey(key.Substring(prefix.Length))), entry.Value); if (!string.IsNullOrEmpty(provider)) { // Return the key-value pair for provider name yield return new DictionaryEntry( string.Format(ProviderKeyFormat, NormalizeKey(key.Substring(prefix.Length))), provider); } } } } ================================================ FILE: src/Config.EnvironmentVariables/EnvironmentVariablesConfigurationSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Extensions.Configuration.EnvironmentVariables { /// /// Represents environment variables as an . /// public class EnvironmentVariablesConfigurationSource : IConfigurationSource { /// /// A prefix used to filter environment variables. /// public string Prefix { get; set; } /// /// Builds the for this source. /// /// The . /// A public IConfigurationProvider Build(IConfigurationBuilder builder) { return new EnvironmentVariablesConfigurationProvider(Prefix); } } } ================================================ FILE: src/Config.EnvironmentVariables/EnvironmentVariablesExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Microsoft.Extensions.Configuration.EnvironmentVariables; namespace Microsoft.Extensions.Configuration { /// /// Extension methods for registering with . /// public static class EnvironmentVariablesExtensions { /// /// Adds an that reads configuration values from environment variables. /// /// The to add to. /// The . public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder) { configurationBuilder.Add(new EnvironmentVariablesConfigurationSource()); return configurationBuilder; } /// /// Adds an that reads configuration values from environment variables /// with a specified prefix. /// /// The to add to. /// The prefix that environment variable names must start with. The prefix will be removed from the environment variable names. /// The . public static IConfigurationBuilder AddEnvironmentVariables( this IConfigurationBuilder configurationBuilder, string prefix) { configurationBuilder.Add(new EnvironmentVariablesConfigurationSource { Prefix = prefix }); return configurationBuilder; } /// /// Adds an that reads configuration values from environment variables. /// /// The to add to. /// Configures the source. /// The . public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder builder, Action configureSource) => builder.Add(configureSource); } } ================================================ FILE: src/Config.EnvironmentVariables/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.Extensions.Configuration.EnvironmentVariables.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] ================================================ FILE: src/Config.EnvironmentVariables/baseline.netcore.json ================================================ { "AssemblyIdentity": "Microsoft.Extensions.Configuration.EnvironmentVariables, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.Configuration.EnvironmentVariablesExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "AddEnvironmentVariables", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddEnvironmentVariables", "Parameters": [ { "Name": "configurationBuilder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "prefix", "Type": "System.String" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddEnvironmentVariables", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "configureSource", "Type": "System.Action" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.EnvironmentVariables.EnvironmentVariablesConfigurationProvider", "Visibility": "Public", "Kind": "Class", "BaseType": "Microsoft.Extensions.Configuration.ConfigurationProvider", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Load", "Parameters": [], "ReturnType": "System.Void", "Virtual": true, "Override": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { "Name": "prefix", "Type": "System.String" } ], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.EnvironmentVariables.EnvironmentVariablesConfigurationSource", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfigurationSource" ], "Members": [ { "Kind": "Method", "Name": "get_Prefix", "Parameters": [], "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Prefix", "Parameters": [ { "Name": "value", "Type": "System.String" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Build", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSource", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] } ] } ================================================ FILE: src/Config.FileExtensions/Config.FileExtensions.csproj ================================================  Microsoft.Extensions.Configuration.FileExtensions Microsoft.Extensions.Configuration.FileExtensions Extension methods for configuring file-based configuration providers for Microsoft.Extensions.Configuration. netstandard2.0 ================================================ FILE: src/Config.FileExtensions/FileConfigurationExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Microsoft.Extensions.FileProviders; namespace Microsoft.Extensions.Configuration { /// /// Extension methods for . /// public static class FileConfigurationExtensions { private static string FileProviderKey = "FileProvider"; private static string FileLoadExceptionHandlerKey = "FileLoadExceptionHandler"; /// /// Sets the default to be used for file-based providers. /// /// The to add to. /// The default file provider instance. /// The . public static IConfigurationBuilder SetFileProvider(this IConfigurationBuilder builder, IFileProvider fileProvider) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } builder.Properties[FileProviderKey] = fileProvider ?? throw new ArgumentNullException(nameof(fileProvider)); return builder; } /// /// Gets the default to be used for file-based providers. /// /// The . /// The . public static IFileProvider GetFileProvider(this IConfigurationBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (builder.Properties.TryGetValue(FileProviderKey, out object provider)) { return builder.Properties[FileProviderKey] as IFileProvider; } return new PhysicalFileProvider(AppContext.BaseDirectory ?? string.Empty); } /// /// Sets the FileProvider for file-based providers to a PhysicalFileProvider with the base path. /// /// The to add to. /// The absolute path of file-based providers. /// The . public static IConfigurationBuilder SetBasePath(this IConfigurationBuilder builder, string basePath) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (basePath == null) { throw new ArgumentNullException(nameof(basePath)); } return builder.SetFileProvider(new PhysicalFileProvider(basePath)); } /// /// Sets a default action to be invoked for file-based providers when an error occurs. /// /// The to add to. /// The Action to be invoked on a file load exception. /// The . public static IConfigurationBuilder SetFileLoadExceptionHandler(this IConfigurationBuilder builder, Action handler) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } builder.Properties[FileLoadExceptionHandlerKey] = handler; return builder; } /// /// Gets the default to be used for file-based providers. /// /// The . /// The . public static Action GetFileLoadExceptionHandler(this IConfigurationBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (builder.Properties.TryGetValue(FileLoadExceptionHandlerKey, out object handler)) { return builder.Properties[FileLoadExceptionHandlerKey] as Action; } return null; } } } ================================================ FILE: src/Config.FileExtensions/FileConfigurationProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using Microsoft.Extensions.Primitives; namespace Microsoft.Extensions.Configuration { /// /// Base class for file based . /// public abstract class FileConfigurationProvider : ConfigurationProvider { /// /// Initializes a new instance with the specified source. /// /// The source settings. public FileConfigurationProvider(FileConfigurationSource source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } Source = source; if (Source.ReloadOnChange && Source.FileProvider != null) { ChangeToken.OnChange( () => Source.FileProvider.Watch(Source.Path), () => { Thread.Sleep(Source.ReloadDelay); Load(reload: true); }); } } /// /// The source settings for this provider. /// public FileConfigurationSource Source { get; } private void Load(bool reload) { var file = Source.FileProvider?.GetFileInfo(Source.Path); if (file == null || !file.Exists) { if (Source.Optional || reload) // Always optional on reload { Data = new Dictionary(StringComparer.OrdinalIgnoreCase); } else { var error = new StringBuilder($"The configuration file '{Source.Path}' was not found and is not optional."); if (!string.IsNullOrEmpty(file?.PhysicalPath)) { error.Append($" The physical path is '{file.PhysicalPath}'."); } throw new FileNotFoundException(error.ToString()); } } else { // Always create new Data on reload to drop old keys if (reload) { Data = new Dictionary(StringComparer.OrdinalIgnoreCase); } using (var stream = file.CreateReadStream()) { try { Load(stream); } catch (Exception e) { bool ignoreException = false; if (Source.OnLoadException != null) { var exceptionContext = new FileLoadExceptionContext { Provider = this, Exception = e }; Source.OnLoadException.Invoke(exceptionContext); ignoreException = exceptionContext.Ignore; } if (!ignoreException) { throw e; } } } } // REVIEW: Should we raise this in the base as well / instead? OnReload(); } /// /// Loads the contents of the file at . /// /// If Optional is false on the source and a /// file does not exist at specified Path. public override void Load() { Load(reload: false); } /// /// Loads this provider's data from a stream. /// /// The stream to read. public abstract void Load(Stream stream); } } ================================================ FILE: src/Config.FileExtensions/FileConfigurationSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using Microsoft.Extensions.FileProviders; namespace Microsoft.Extensions.Configuration { /// /// Represents a base class for file based . /// public abstract class FileConfigurationSource : IConfigurationSource { /// /// Used to access the contents of the file. /// public IFileProvider FileProvider { get; set; } /// /// The path to the file. /// public string Path { get; set; } /// /// Determines if loading the file is optional. /// public bool Optional { get; set; } /// /// Determines whether the source will be loaded if the underlying file changes. /// public bool ReloadOnChange { get; set; } /// /// Number of milliseconds that reload will wait before calling Load. This helps /// avoid triggering reload before a file is completely written. Default is 250. /// public int ReloadDelay { get; set; } = 250; /// /// Will be called if an uncaught exception occurs in FileConfigurationProvider.Load. /// public Action OnLoadException { get; set; } /// /// Builds the for this source. /// /// The . /// A public abstract IConfigurationProvider Build(IConfigurationBuilder builder); /// /// Called to use any default settings on the builder like the FileProvider or FileLoadExceptionHandler. /// /// The . public void EnsureDefaults(IConfigurationBuilder builder) { FileProvider = FileProvider ?? builder.GetFileProvider(); OnLoadException = OnLoadException ?? builder.GetFileLoadExceptionHandler(); } /// /// If no file provider has been set, for absolute Path, this will creates a physical file provider /// for the nearest existing directory. /// public void ResolveFileProvider() { if (FileProvider == null && !string.IsNullOrEmpty(Path) && System.IO.Path.IsPathRooted(Path)) { var directory = System.IO.Path.GetDirectoryName(Path); var pathToFile = System.IO.Path.GetFileName(Path); while (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { pathToFile = System.IO.Path.Combine(System.IO.Path.GetFileName(directory), pathToFile); directory = System.IO.Path.GetDirectoryName(directory); } if (Directory.Exists(directory)) { FileProvider = new PhysicalFileProvider(directory); Path = pathToFile; } } } } } ================================================ FILE: src/Config.FileExtensions/FileLoadExceptionContext.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Extensions.Configuration { /// /// Contains information about a file load exception. /// public class FileLoadExceptionContext { /// /// The that caused the exception. /// public FileConfigurationProvider Provider { get; set; } /// /// The exception that occured in Load. /// public Exception Exception { get; set; } /// /// If true, the exception will not be rethrown. /// public bool Ignore { get; set; } } } ================================================ FILE: src/Config.FileExtensions/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.Extensions.Configuration.FileExtensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] ================================================ FILE: src/Config.FileExtensions/Properties/Resources.Designer.cs ================================================ // namespace Microsoft.Extensions.Configuration.FileExtensions { using System.Globalization; using System.Reflection; using System.Resources; internal static class Resources { private static readonly ResourceManager _resourceManager = new ResourceManager("Microsoft.Extensions.Configuration.FileExtensions.Resources", typeof(Resources).GetTypeInfo().Assembly); /// /// The expected physical path was '{0}'. /// internal static string Error_ExpectedPhysicalPath { get => GetString("Error_ExpectedPhysicalPath"); } /// /// The expected physical path was '{0}'. /// internal static string FormatError_ExpectedPhysicalPath(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_ExpectedPhysicalPath"), p0); /// /// The configuration file '{0}' was not found and is not optional. /// internal static string Error_FileNotFound { get => GetString("Error_FileNotFound"); } /// /// The configuration file '{0}' was not found and is not optional. /// internal static string FormatError_FileNotFound(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_FileNotFound"), p0); private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); System.Diagnostics.Debug.Assert(value != null); if (formatterNames != null) { for (var i = 0; i < formatterNames.Length; i++) { value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); } } return value; } } } ================================================ FILE: src/Config.FileExtensions/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 The expected physical path was '{0}'. The configuration file '{0}' was not found and is not optional. ================================================ FILE: src/Config.FileExtensions/baseline.netcore.json ================================================ { "AssemblyIdentity": "Microsoft.Extensions.Configuration.FileExtensions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.Configuration.FileConfigurationExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "SetFileProvider", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "fileProvider", "Type": "Microsoft.Extensions.FileProviders.IFileProvider" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetFileProvider", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.FileProviders.IFileProvider", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "SetBasePath", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "basePath", "Type": "System.String" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "SetFileLoadExceptionHandler", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "handler", "Type": "System.Action" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "GetFileLoadExceptionHandler", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "System.Action", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.FileConfigurationProvider", "Visibility": "Public", "Kind": "Class", "Abstract": true, "BaseType": "Microsoft.Extensions.Configuration.ConfigurationProvider", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Load", "Parameters": [], "ReturnType": "System.Void", "Virtual": true, "Override": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Source", "Parameters": [], "ReturnType": "Microsoft.Extensions.Configuration.FileConfigurationSource", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Load", "Parameters": [ { "Name": "stream", "Type": "System.IO.Stream" } ], "ReturnType": "System.Void", "Virtual": true, "Abstract": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { "Name": "source", "Type": "Microsoft.Extensions.Configuration.FileConfigurationSource" } ], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.FileConfigurationSource", "Visibility": "Public", "Kind": "Class", "Abstract": true, "ImplementedInterfaces": [ "Microsoft.Extensions.Configuration.IConfigurationSource" ], "Members": [ { "Kind": "Method", "Name": "get_FileProvider", "Parameters": [], "ReturnType": "Microsoft.Extensions.FileProviders.IFileProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_FileProvider", "Parameters": [ { "Name": "value", "Type": "Microsoft.Extensions.FileProviders.IFileProvider" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Path", "Parameters": [], "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Path", "Parameters": [ { "Name": "value", "Type": "System.String" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Optional", "Parameters": [], "ReturnType": "System.Boolean", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Optional", "Parameters": [ { "Name": "value", "Type": "System.Boolean" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_ReloadOnChange", "Parameters": [], "ReturnType": "System.Boolean", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_ReloadOnChange", "Parameters": [ { "Name": "value", "Type": "System.Boolean" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_ReloadDelay", "Parameters": [], "ReturnType": "System.Int32", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_ReloadDelay", "Parameters": [ { "Name": "value", "Type": "System.Int32" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_OnLoadException", "Parameters": [], "ReturnType": "System.Action", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_OnLoadException", "Parameters": [ { "Name": "value", "Type": "System.Action" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "Build", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Virtual": true, "Abstract": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSource", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "EnsureDefaults", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "ResolveFileProvider", "Parameters": [], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Protected", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.FileLoadExceptionContext", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "get_Provider", "Parameters": [], "ReturnType": "Microsoft.Extensions.Configuration.FileConfigurationProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Provider", "Parameters": [ { "Name": "value", "Type": "Microsoft.Extensions.Configuration.FileConfigurationProvider" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Exception", "Parameters": [], "ReturnType": "System.Exception", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Exception", "Parameters": [ { "Name": "value", "Type": "System.Exception" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "get_Ignore", "Parameters": [], "ReturnType": "System.Boolean", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "set_Ignore", "Parameters": [ { "Name": "value", "Type": "System.Boolean" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] } ] } ================================================ FILE: src/Config.Ini/Config.Ini.csproj ================================================  Microsoft.Extensions.Configuration.Ini Microsoft.Extensions.Configuration.Ini INI configuration provider implementation for Microsoft.Extensions.Configuration. netstandard2.0 $(PackageTags);ini ================================================ FILE: src/Config.Ini/IniConfigurationExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using Microsoft.Extensions.Configuration.Ini; using Microsoft.Extensions.FileProviders; namespace Microsoft.Extensions.Configuration { /// /// Extension methods for adding . /// public static class IniConfigurationExtensions { /// /// Adds the INI configuration provider at to . /// /// The to add to. /// Path relative to the base path stored in /// of . /// The . public static IConfigurationBuilder AddIniFile(this IConfigurationBuilder builder, string path) { return AddIniFile(builder, provider: null, path: path, optional: false, reloadOnChange: false); } /// /// Adds the INI configuration provider at to . /// /// The to add to. /// Path relative to the base path stored in /// of . /// Whether the file is optional. /// The . public static IConfigurationBuilder AddIniFile(this IConfigurationBuilder builder, string path, bool optional) { return AddIniFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false); } /// /// Adds the INI configuration provider at to . /// /// The to add to. /// Path relative to the base path stored in /// of . /// Whether the file is optional. /// Whether the configuration should be reloaded if the file changes. /// The . public static IConfigurationBuilder AddIniFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) { return AddIniFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange); } /// /// Adds a INI configuration source to . /// /// The to add to. /// The to use to access the file. /// Path relative to the base path stored in /// of . /// Whether the file is optional. /// Whether the configuration should be reloaded if the file changes. /// The . public static IConfigurationBuilder AddIniFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (string.IsNullOrEmpty(path)) { throw new ArgumentException(Resources.Error_InvalidFilePath, nameof(path)); } return builder.AddIniFile(s => { s.FileProvider = provider; s.Path = path; s.Optional = optional; s.ReloadOnChange = reloadOnChange; s.ResolveFileProvider(); }); } /// /// Adds a INI configuration source to . /// /// The to add to. /// Configures the source. /// The . public static IConfigurationBuilder AddIniFile(this IConfigurationBuilder builder, Action configureSource) => builder.Add(configureSource); } } ================================================ FILE: src/Config.Ini/IniConfigurationProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.IO; namespace Microsoft.Extensions.Configuration.Ini { /// /// An INI file based . /// Files are simple line structures (INI Files on Wikipedia) /// /// /// [Section:Header] /// key1=value1 /// key2 = " value2 " /// ; comment /// # comment /// / comment /// public class IniConfigurationProvider : FileConfigurationProvider { /// /// Initializes a new instance with the specified source. /// /// The source settings. public IniConfigurationProvider(IniConfigurationSource source) : base(source) { } /// /// Loads the INI data from a stream. /// /// The stream to read. public override void Load(Stream stream) { var data = new Dictionary(StringComparer.OrdinalIgnoreCase); using (var reader = new StreamReader(stream)) { var sectionPrefix = string.Empty; while (reader.Peek() != -1) { var rawLine = reader.ReadLine(); var line = rawLine.Trim(); // Ignore blank lines if (string.IsNullOrWhiteSpace(line)) { continue; } // Ignore comments if (line[0] == ';' || line[0] == '#' || line[0] == '/') { continue; } // [Section:header] if (line[0] == '[' && line[line.Length - 1] == ']') { // remove the brackets sectionPrefix = line.Substring(1, line.Length - 2) + ConfigurationPath.KeyDelimiter; continue; } // key = value OR "value" int separator = line.IndexOf('='); if (separator < 0) { throw new FormatException(Resources.FormatError_UnrecognizedLineFormat(rawLine)); } string key = sectionPrefix + line.Substring(0, separator).Trim(); string value = line.Substring(separator + 1).Trim(); // Remove quotes if (value.Length > 1 && value[0] == '"' && value[value.Length - 1] == '"') { value = value.Substring(1, value.Length - 2); } if (data.ContainsKey(key)) { throw new FormatException(Resources.FormatError_KeyIsDuplicated(key)); } data[key] = value; } } Data = data; } } } ================================================ FILE: src/Config.Ini/IniConfigurationSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Extensions.Configuration.Ini { /// /// Represents an INI file as an . /// Files are simple line structures (INI Files on Wikipedia) /// /// /// [Section:Header] /// key1=value1 /// key2 = " value2 " /// ; comment /// # comment /// / comment /// public class IniConfigurationSource : FileConfigurationSource { /// /// Builds the for this source. /// /// The . /// An public override IConfigurationProvider Build(IConfigurationBuilder builder) { EnsureDefaults(builder); return new IniConfigurationProvider(this); } } } ================================================ FILE: src/Config.Ini/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.Extensions.Configuration.Ini.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] ================================================ FILE: src/Config.Ini/Properties/Resources.Designer.cs ================================================ // namespace Microsoft.Extensions.Configuration.Ini { using System.Globalization; using System.Reflection; using System.Resources; internal static class Resources { private static readonly ResourceManager _resourceManager = new ResourceManager("Microsoft.Extensions.Configuration.Ini.Resources", typeof(Resources).GetTypeInfo().Assembly); /// /// File path must be a non-empty string. /// internal static string Error_InvalidFilePath { get => GetString("Error_InvalidFilePath"); } /// /// File path must be a non-empty string. /// internal static string FormatError_InvalidFilePath() => GetString("Error_InvalidFilePath"); /// /// A duplicate key '{0}' was found. /// internal static string Error_KeyIsDuplicated { get => GetString("Error_KeyIsDuplicated"); } /// /// A duplicate key '{0}' was found. /// internal static string FormatError_KeyIsDuplicated(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_KeyIsDuplicated"), p0); /// /// Unrecognized line format: '{0}'. /// internal static string Error_UnrecognizedLineFormat { get => GetString("Error_UnrecognizedLineFormat"); } /// /// Unrecognized line format: '{0}'. /// internal static string FormatError_UnrecognizedLineFormat(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_UnrecognizedLineFormat"), p0); private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); System.Diagnostics.Debug.Assert(value != null); if (formatterNames != null) { for (var i = 0; i < formatterNames.Length; i++) { value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); } } return value; } } } ================================================ FILE: src/Config.Ini/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 File path must be a non-empty string. A duplicate key '{0}' was found. Unrecognized line format: '{0}'. ================================================ FILE: src/Config.Ini/baseline.netcore.json ================================================ { "AssemblyIdentity": "Microsoft.Extensions.Configuration.Ini, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.Configuration.IniConfigurationExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "AddIniFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "path", "Type": "System.String" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddIniFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "path", "Type": "System.String" }, { "Name": "optional", "Type": "System.Boolean" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddIniFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "path", "Type": "System.String" }, { "Name": "optional", "Type": "System.Boolean" }, { "Name": "reloadOnChange", "Type": "System.Boolean" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddIniFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "provider", "Type": "Microsoft.Extensions.FileProviders.IFileProvider" }, { "Name": "path", "Type": "System.String" }, { "Name": "optional", "Type": "System.Boolean" }, { "Name": "reloadOnChange", "Type": "System.Boolean" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddIniFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "configureSource", "Type": "System.Action" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.Ini.IniConfigurationProvider", "Visibility": "Public", "Kind": "Class", "BaseType": "Microsoft.Extensions.Configuration.FileConfigurationProvider", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Load", "Parameters": [ { "Name": "stream", "Type": "System.IO.Stream" } ], "ReturnType": "System.Void", "Virtual": true, "Override": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { "Name": "source", "Type": "Microsoft.Extensions.Configuration.Ini.IniConfigurationSource" } ], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.Ini.IniConfigurationSource", "Visibility": "Public", "Kind": "Class", "BaseType": "Microsoft.Extensions.Configuration.FileConfigurationSource", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Build", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Virtual": true, "Override": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSource", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] } ] } ================================================ FILE: src/Config.Json/Config.Json.csproj ================================================  Microsoft.Extensions.Configuration.Json Microsoft.Extensions.Configuration.Json JSON configuration provider implementation for Microsoft.Extensions.Configuration. netstandard2.0 $(PackageTags);json ================================================ FILE: src/Config.Json/JsonConfigurationExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using Microsoft.Extensions.Configuration.Json; using Microsoft.Extensions.FileProviders; namespace Microsoft.Extensions.Configuration { /// /// Extension methods for adding . /// public static class JsonConfigurationExtensions { /// /// Adds the JSON configuration provider at to . /// /// The to add to. /// Path relative to the base path stored in /// of . /// The . public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path) { return AddJsonFile(builder, provider: null, path: path, optional: false, reloadOnChange: false); } /// /// Adds the JSON configuration provider at to . /// /// The to add to. /// Path relative to the base path stored in /// of . /// Whether the file is optional. /// The . public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path, bool optional) { return AddJsonFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false); } /// /// Adds the JSON configuration provider at to . /// /// The to add to. /// Path relative to the base path stored in /// of . /// Whether the file is optional. /// Whether the configuration should be reloaded if the file changes. /// The . public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) { return AddJsonFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange); } /// /// Adds a JSON configuration source to . /// /// The to add to. /// The to use to access the file. /// Path relative to the base path stored in /// of . /// Whether the file is optional. /// Whether the configuration should be reloaded if the file changes. /// The . public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (string.IsNullOrEmpty(path)) { throw new ArgumentException(Resources.Error_InvalidFilePath, nameof(path)); } return builder.AddJsonFile(s => { s.FileProvider = provider; s.Path = path; s.Optional = optional; s.ReloadOnChange = reloadOnChange; s.ResolveFileProvider(); }); } /// /// Adds a JSON configuration source to . /// /// The to add to. /// Configures the source. /// The . public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, Action configureSource) => builder.Add(configureSource); } } ================================================ FILE: src/Config.Json/JsonConfigurationFileParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Microsoft.Extensions.Configuration.Json { internal class JsonConfigurationFileParser { private JsonConfigurationFileParser() { } private readonly IDictionary _data = new SortedDictionary(StringComparer.OrdinalIgnoreCase); private readonly Stack _context = new Stack(); private string _currentPath; private JsonTextReader _reader; public static IDictionary Parse(Stream input) => new JsonConfigurationFileParser().ParseStream(input); private IDictionary ParseStream(Stream input) { _data.Clear(); _reader = new JsonTextReader(new StreamReader(input)); _reader.DateParseHandling = DateParseHandling.None; var jsonConfig = JObject.Load(_reader); VisitJObject(jsonConfig); return _data; } private void VisitJObject(JObject jObject) { foreach (var property in jObject.Properties()) { EnterContext(property.Name); VisitProperty(property); ExitContext(); } } private void VisitProperty(JProperty property) { VisitToken(property.Value); } private void VisitToken(JToken token) { switch (token.Type) { case JTokenType.Object: VisitJObject(token.Value()); break; case JTokenType.Array: VisitArray(token.Value()); break; case JTokenType.Integer: case JTokenType.Float: case JTokenType.String: case JTokenType.Boolean: case JTokenType.Bytes: case JTokenType.Raw: case JTokenType.Null: VisitPrimitive(token.Value()); break; default: throw new FormatException(Resources.FormatError_UnsupportedJSONToken( _reader.TokenType, _reader.Path, _reader.LineNumber, _reader.LinePosition)); } } private void VisitArray(JArray array) { for (int index = 0; index < array.Count; index++) { EnterContext(index.ToString()); VisitToken(array[index]); ExitContext(); } } private void VisitPrimitive(JValue data) { var key = _currentPath; if (_data.ContainsKey(key)) { throw new FormatException(Resources.FormatError_KeyIsDuplicated(key)); } _data[key] = data.ToString(CultureInfo.InvariantCulture); } private void EnterContext(string context) { _context.Push(context); _currentPath = ConfigurationPath.Combine(_context.Reverse()); } private void ExitContext() { _context.Pop(); _currentPath = ConfigurationPath.Combine(_context.Reverse()); } } } ================================================ FILE: src/Config.Json/JsonConfigurationProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.IO; using System.Linq; using Newtonsoft.Json; namespace Microsoft.Extensions.Configuration.Json { /// /// A JSON file based . /// public class JsonConfigurationProvider : FileConfigurationProvider { /// /// Initializes a new instance with the specified source. /// /// The source settings. public JsonConfigurationProvider(JsonConfigurationSource source) : base(source) { } /// /// Loads the JSON data from a stream. /// /// The stream to read. public override void Load(Stream stream) { try { Data = JsonConfigurationFileParser.Parse(stream); } catch (JsonReaderException e) { string errorLine = string.Empty; if (stream.CanSeek) { stream.Seek(0, SeekOrigin.Begin); IEnumerable fileContent; using (var streamReader = new StreamReader(stream)) { fileContent = ReadLines(streamReader); errorLine = RetrieveErrorContext(e, fileContent); } } throw new FormatException(Resources.FormatError_JSONParseError(e.LineNumber, errorLine), e); } } private static string RetrieveErrorContext(JsonReaderException e, IEnumerable fileContent) { string errorLine = null; if (e.LineNumber >= 2) { var errorContext = fileContent.Skip(e.LineNumber - 2).Take(2).ToList(); // Handle situations when the line number reported is out of bounds if (errorContext.Count() >= 2) { errorLine = errorContext[0].Trim() + Environment.NewLine + errorContext[1].Trim(); } } if (string.IsNullOrEmpty(errorLine)) { var possibleLineContent = fileContent.Skip(e.LineNumber - 1).FirstOrDefault(); errorLine = possibleLineContent ?? string.Empty; } return errorLine; } private static IEnumerable ReadLines(StreamReader streamReader) { string line; do { line = streamReader.ReadLine(); yield return line; } while (line != null); } } } ================================================ FILE: src/Config.Json/JsonConfigurationSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Extensions.Configuration.Json { /// /// Represents a JSON file as an . /// public class JsonConfigurationSource : FileConfigurationSource { /// /// Builds the for this source. /// /// The . /// A public override IConfigurationProvider Build(IConfigurationBuilder builder) { EnsureDefaults(builder); return new JsonConfigurationProvider(this); } } } ================================================ FILE: src/Config.Json/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.Extensions.Configuration.Json.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] ================================================ FILE: src/Config.Json/Properties/Resources.Designer.cs ================================================ // namespace Microsoft.Extensions.Configuration.Json { using System.Globalization; using System.Reflection; using System.Resources; internal static class Resources { private static readonly ResourceManager _resourceManager = new ResourceManager("Microsoft.Extensions.Configuration.Json.Resources", typeof(Resources).GetTypeInfo().Assembly); /// /// File path must be a non-empty string. /// internal static string Error_InvalidFilePath { get => GetString("Error_InvalidFilePath"); } /// /// File path must be a non-empty string. /// internal static string FormatError_InvalidFilePath() => GetString("Error_InvalidFilePath"); /// /// Could not parse the JSON file. Error on line number '{0}': '{1}'. /// internal static string Error_JSONParseError { get => GetString("Error_JSONParseError"); } /// /// Could not parse the JSON file. Error on line number '{0}': '{1}'. /// internal static string FormatError_JSONParseError(object p0, object p1) => string.Format(CultureInfo.CurrentCulture, GetString("Error_JSONParseError"), p0, p1); /// /// A duplicate key '{0}' was found. /// internal static string Error_KeyIsDuplicated { get => GetString("Error_KeyIsDuplicated"); } /// /// A duplicate key '{0}' was found. /// internal static string FormatError_KeyIsDuplicated(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_KeyIsDuplicated"), p0); /// /// Unsupported JSON token '{0}' was found. Path '{1}', line {2} position {3}. /// internal static string Error_UnsupportedJSONToken { get => GetString("Error_UnsupportedJSONToken"); } /// /// Unsupported JSON token '{0}' was found. Path '{1}', line {2} position {3}. /// internal static string FormatError_UnsupportedJSONToken(object p0, object p1, object p2, object p3) => string.Format(CultureInfo.CurrentCulture, GetString("Error_UnsupportedJSONToken"), p0, p1, p2, p3); private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); System.Diagnostics.Debug.Assert(value != null); if (formatterNames != null) { for (var i = 0; i < formatterNames.Length; i++) { value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); } } return value; } } } ================================================ FILE: src/Config.Json/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 File path must be a non-empty string. Could not parse the JSON file. Error on line number '{0}': '{1}'. A duplicate key '{0}' was found. Unsupported JSON token '{0}' was found. Path '{1}', line {2} position {3}. ================================================ FILE: src/Config.Json/baseline.netcore.json ================================================ { "AssemblyIdentity": "Microsoft.Extensions.Configuration.Json, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.Configuration.JsonConfigurationExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "AddJsonFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "path", "Type": "System.String" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddJsonFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "path", "Type": "System.String" }, { "Name": "optional", "Type": "System.Boolean" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddJsonFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "path", "Type": "System.String" }, { "Name": "optional", "Type": "System.Boolean" }, { "Name": "reloadOnChange", "Type": "System.Boolean" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddJsonFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "provider", "Type": "Microsoft.Extensions.FileProviders.IFileProvider" }, { "Name": "path", "Type": "System.String" }, { "Name": "optional", "Type": "System.Boolean" }, { "Name": "reloadOnChange", "Type": "System.Boolean" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddJsonFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "configureSource", "Type": "System.Action" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider", "Visibility": "Public", "Kind": "Class", "BaseType": "Microsoft.Extensions.Configuration.FileConfigurationProvider", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Load", "Parameters": [ { "Name": "stream", "Type": "System.IO.Stream" } ], "ReturnType": "System.Void", "Virtual": true, "Override": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { "Name": "source", "Type": "Microsoft.Extensions.Configuration.Json.JsonConfigurationSource" } ], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.Json.JsonConfigurationSource", "Visibility": "Public", "Kind": "Class", "BaseType": "Microsoft.Extensions.Configuration.FileConfigurationSource", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Build", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Virtual": true, "Override": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSource", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] } ] } ================================================ FILE: src/Config.KeyPerFile/Config.KeyPerFile.csproj ================================================  Microsoft.Extensions.Configuration.KeyPerFile Microsoft.Extensions.Configuration.KeyPerFile Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration. netstandard2.0 false ================================================ FILE: src/Config.KeyPerFile/KeyPerFileConfigurationBuilderExtensions.cs ================================================ using System; using System.IO; using Microsoft.Extensions.Configuration.KeyPerFile; using Microsoft.Extensions.FileProviders; namespace Microsoft.Extensions.Configuration { /// /// Extension methods for registering with . /// public static class KeyPerFileConfigurationBuilderExtensions { /// /// Adds configuration using files from a directory. File names are used as the key, /// file contents are used as the value. /// /// The to add to. /// The path to the directory. /// Whether the directory is optional. /// The . public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath, bool optional) => builder.AddKeyPerFile(source => { // Only try to set the file provider if its not optional or the directory exists if (!optional || Directory.Exists(directoryPath)) { source.FileProvider = new PhysicalFileProvider(directoryPath); } source.Optional = optional; }); /// /// Adds configuration using files from a directory. File names are used as the key, /// file contents are used as the value. /// /// The to add to. /// Configures the source. /// The . public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, Action configureSource) => builder.Add(configureSource); } } ================================================ FILE: src/Config.KeyPerFile/KeyPerFileConfigurationProvider.cs ================================================ using System; using System.Collections.Generic; using System.IO; namespace Microsoft.Extensions.Configuration.KeyPerFile { /// /// A that uses a directory's files as configuration key/values. /// public class KeyPerFileConfigurationProvider : ConfigurationProvider { KeyPerFileConfigurationSource Source { get; set; } /// /// Initializes a new instance. /// /// The settings. public KeyPerFileConfigurationProvider(KeyPerFileConfigurationSource source) => Source = source ?? throw new ArgumentNullException(nameof(source)); private static string NormalizeKey(string key) => key.Replace("__", ConfigurationPath.KeyDelimiter); private static string TrimNewLine(string value) => value.EndsWith(Environment.NewLine) ? value.Substring(0, value.Length - Environment.NewLine.Length) : value; /// /// Loads the docker secrets. /// public override void Load() { Data = new Dictionary(StringComparer.OrdinalIgnoreCase); if (Source.FileProvider == null) { if (Source.Optional) { return; } else { throw new DirectoryNotFoundException("A non-null file provider for the directory is required when this source is not optional."); } } var directory = Source.FileProvider.GetDirectoryContents("/"); if (!directory.Exists && !Source.Optional) { throw new DirectoryNotFoundException("The root directory for the FileProvider doesn't exist and is not optional."); } foreach (var file in directory) { if (file.IsDirectory) { continue; } using (var stream = file.CreateReadStream()) using (var streamReader = new StreamReader(stream)) { if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name)) { Data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd())); } } } } } } ================================================ FILE: src/Config.KeyPerFile/KeyPerFileConfigurationSource.cs ================================================ using System; using System.IO; using Microsoft.Extensions.FileProviders; namespace Microsoft.Extensions.Configuration.KeyPerFile { /// /// An used to configure . /// public class KeyPerFileConfigurationSource : IConfigurationSource { /// /// Constructor; /// public KeyPerFileConfigurationSource() => IgnoreCondition = s => IgnorePrefix != null && s.StartsWith(IgnorePrefix); /// /// The FileProvider whos root "/" directory files will be used as configuration data. /// public IFileProvider FileProvider { get; set; } /// /// Files that start with this prefix will be excluded. /// Defaults to "ignore.". /// public string IgnorePrefix { get; set; } = "ignore."; /// /// Used to determine if a file should be ignored using its name. /// Defaults to using the IgnorePrefix. /// public Func IgnoreCondition { get; set; } /// /// If false, will throw if the directory doesn't exist. /// public bool Optional { get; set; } /// /// Builds the for this source. /// /// The . /// A public IConfigurationProvider Build(IConfigurationBuilder builder) => new KeyPerFileConfigurationProvider(this); } } ================================================ FILE: src/Config.KeyPerFile/README.md ================================================  This is a configuration provider that uses a directory's files as data. A file's name is the key and the contents are the value. ================================================ FILE: src/Config.UserSecrets/Config.UserSecrets.csproj ================================================  Microsoft.Extensions.Configuration.UserSecrets Microsoft.Extensions.Configuration.UserSecrets Microsoft.Extensions.Configuration.UserSecrets User secrets configuration provider implementation for Microsoft.Extensions.Configuration. netstandard2.0 $(PackageTags);secrets;usersecrets ================================================ FILE: src/Config.UserSecrets/PathHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; namespace Microsoft.Extensions.Configuration.UserSecrets { /// /// Provides paths for user secrets configuration files. /// public class PathHelper { internal const string SecretsFileName = "secrets.json"; /// /// /// Returns the path to the JSON file that stores user secrets. /// /// /// This uses the current user profile to locate the secrets file on disk in a location outside of source control. /// /// /// The user secret ID. /// The full path to the secret file. public static string GetSecretsPathFromSecretsId(string userSecretsId) { if (string.IsNullOrEmpty(userSecretsId)) { throw new ArgumentException(Resources.Common_StringNullOrEmpty, nameof(userSecretsId)); } var badCharIndex = userSecretsId.IndexOfAny(Path.GetInvalidFileNameChars()); if (badCharIndex != -1) { throw new InvalidOperationException( string.Format( Resources.Error_Invalid_Character_In_UserSecrets_Id, userSecretsId[badCharIndex], badCharIndex)); } const string userSecretsFallbackDir = "DOTNET_USER_SECRETS_FALLBACK_DIR"; // For backwards compat, this checks env vars first before using Env.GetFolderPath var root = Environment.GetEnvironmentVariable("APPDATA") // On Windows it goes to %APPDATA%\Microsoft\UserSecrets\ ?? Environment.GetEnvironmentVariable("HOME") // On Mac/Linux it goes to ~/.microsoft/usersecrets/ ?? Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? Environment.GetEnvironmentVariable(userSecretsFallbackDir); // this fallback is an escape hatch if everything else fails if (string.IsNullOrEmpty(root)) { throw new InvalidOperationException("Could not determine an appropriate location for storing user secrets. Set the " + userSecretsFallbackDir + " environment variable to a folder where user secrets should be stored."); } if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPDATA"))) { return Path.Combine(root, "Microsoft", "UserSecrets", userSecretsId, SecretsFileName); } else { return Path.Combine(root, ".microsoft", "usersecrets", userSecretsId, SecretsFileName); } } } } ================================================ FILE: src/Config.UserSecrets/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.Extensions.Configuration.UserSecrets.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] ================================================ FILE: src/Config.UserSecrets/Properties/Resources.Designer.cs ================================================ // namespace Microsoft.Extensions.Configuration.UserSecrets { using System.Globalization; using System.Reflection; using System.Resources; internal static class Resources { private static readonly ResourceManager _resourceManager = new ResourceManager("Microsoft.Extensions.Configuration.UserSecrets.Resources", typeof(Resources).GetTypeInfo().Assembly); /// /// Value cannot be null or an empty string. /// internal static string Common_StringNullOrEmpty { get => GetString("Common_StringNullOrEmpty"); } /// /// Value cannot be null or an empty string. /// internal static string FormatCommon_StringNullOrEmpty() => GetString("Common_StringNullOrEmpty"); /// /// Invalid character '{0}' found in the user secrets ID at index '{1}'. /// internal static string Error_Invalid_Character_In_UserSecrets_Id { get => GetString("Error_Invalid_Character_In_UserSecrets_Id"); } /// /// Invalid character '{0}' found in the user secrets ID at index '{1}'. /// internal static string FormatError_Invalid_Character_In_UserSecrets_Id(object p0, object p1) => string.Format(CultureInfo.CurrentCulture, GetString("Error_Invalid_Character_In_UserSecrets_Id"), p0, p1); /// /// Could not find 'UserSecretsIdAttribute' on assembly '{0}'. /// Check that the project for '{0}' has set the 'UserSecretsId' build property. /// If the 'UserSecretsId' property is already set then add a reference to the Microsoft.Extensions.Configuration.UserSecrets package. /// internal static string Error_Missing_UserSecretsIdAttribute { get => GetString("Error_Missing_UserSecretsIdAttribute"); } /// /// Could not find 'UserSecretsIdAttribute' on assembly '{0}'. /// Check that the project for '{0}' has set the 'UserSecretsId' build property. /// If the 'UserSecretsId' property is already set then add a reference to the Microsoft.Extensions.Configuration.UserSecrets package. /// internal static string FormatError_Missing_UserSecretsIdAttribute(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_Missing_UserSecretsIdAttribute"), p0); private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); System.Diagnostics.Debug.Assert(value != null); if (formatterNames != null) { for (var i = 0; i < formatterNames.Length; i++) { value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); } } return value; } } } ================================================ FILE: src/Config.UserSecrets/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Value cannot be null or an empty string. Invalid character '{0}' found in the user secrets ID at index '{1}'. Could not find 'UserSecretsIdAttribute' on assembly '{0}'. Check that the project for '{0}' has set the 'UserSecretsId' build property. If the 'UserSecretsId' property is already set then add a reference to the Microsoft.Extensions.Configuration.UserSecrets package. ================================================ FILE: src/Config.UserSecrets/UserSecretsConfigurationExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using System.Reflection; using Microsoft.Extensions.Configuration.UserSecrets; using Microsoft.Extensions.FileProviders; namespace Microsoft.Extensions.Configuration { /// /// Configuration extensions for adding user secrets configuration source. /// public static class UserSecretsConfigurationExtensions { /// /// /// Adds the user secrets configuration source. Searches the assembly that contains type /// for an instance of , which specifies a user secrets ID. /// /// /// A user secrets ID is unique value used to store and identify a collection of secret configuration values. /// /// /// The configuration builder. /// The type from the assembly to search for an instance of . /// Thrown when the assembly containing does not have . /// The configuration builder. public static IConfigurationBuilder AddUserSecrets(this IConfigurationBuilder configuration) where T : class => configuration.AddUserSecrets(optional: false); /// /// /// Adds the user secrets configuration source. Searches the assembly that contains type /// for an instance of , which specifies a user secrets ID. /// /// /// A user secrets ID is unique value used to store and identify a collection of secret configuration values. /// /// /// The configuration builder. /// Whether loading secrets is optional. When false, this method may throw. /// Thrown when is false and the assembly containing does not have a valid . /// The type from the assembly to search for an instance of . /// The configuration builder. public static IConfigurationBuilder AddUserSecrets(this IConfigurationBuilder configuration, bool optional) where T : class => configuration.AddUserSecrets(typeof(T).GetTypeInfo().Assembly, optional); /// /// /// Adds the user secrets configuration source. This searches for an instance /// of , which specifies a user secrets ID. /// /// /// A user secrets ID is unique value used to store and identify a collection of secret configuration values. /// /// /// The configuration builder. /// The assembly with the . /// Thrown when does not have a valid /// The configuration builder. public static IConfigurationBuilder AddUserSecrets(this IConfigurationBuilder configuration, Assembly assembly) => configuration.AddUserSecrets(assembly, optional: false); /// /// /// Adds the user secrets configuration source. This searches for an instance /// of , which specifies a user secrets ID. /// /// /// A user secrets ID is unique value used to store and identify a collection of secret configuration values. /// /// /// The configuration builder. /// The assembly with the . /// Whether loading secrets is optional. When false, this method may throw. /// Thrown when is false and does not have a valid . /// The configuration builder. public static IConfigurationBuilder AddUserSecrets(this IConfigurationBuilder configuration, Assembly assembly, bool optional) { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } if (assembly == null) { throw new ArgumentNullException(nameof(assembly)); } var attribute = assembly.GetCustomAttribute(); if (attribute != null) { return AddUserSecrets(configuration, attribute.UserSecretsId); } if (!optional) { throw new InvalidOperationException(Resources.FormatError_Missing_UserSecretsIdAttribute(assembly.GetName().Name)); } return configuration; } /// /// /// Adds the user secrets configuration source with specified user secrets ID. /// /// /// A user secrets ID is unique value used to store and identify a collection of secret configuration values. /// /// /// The configuration builder. /// The user secrets ID. /// The configuration builder. public static IConfigurationBuilder AddUserSecrets(this IConfigurationBuilder configuration, string userSecretsId) { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } if (userSecretsId == null) { throw new ArgumentNullException(nameof(userSecretsId)); } return AddSecretsFile(configuration, PathHelper.GetSecretsPathFromSecretsId(userSecretsId)); } private static IConfigurationBuilder AddSecretsFile(IConfigurationBuilder configuration, string secretPath) { var directoryPath = Path.GetDirectoryName(secretPath); var fileProvider = Directory.Exists(directoryPath) ? new PhysicalFileProvider(directoryPath) : null; return configuration.AddJsonFile(fileProvider, PathHelper.SecretsFileName, optional: true, reloadOnChange: false); } } } ================================================ FILE: src/Config.UserSecrets/UserSecretsIdAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Extensions.Configuration.UserSecrets { /// /// /// Represents the user secrets ID. /// /// /// In most cases, this attribute is automatically generated during compilation by MSBuild targets /// included in the UserSecrets NuGet package. These targets use the MSBuild property 'UserSecretsId' /// to set the value for . /// /// [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] public class UserSecretsIdAttribute : Attribute { /// /// Initializes an instance of . /// /// The user secrets ID. public UserSecretsIdAttribute(string userSecretId) { if (string.IsNullOrEmpty(userSecretId)) { throw new ArgumentException(Resources.Common_StringNullOrEmpty, nameof(userSecretId)); } UserSecretsId = userSecretId; } /// /// The user secrets ID. /// public string UserSecretsId { get; } } } ================================================ FILE: src/Config.UserSecrets/baseline.netcore.json ================================================ { "AssemblyIdentity": "Microsoft.Extensions.Configuration.UserSecrets, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.Configuration.UserSecretsConfigurationExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "AddUserSecrets", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [ { "ParameterName": "T", "ParameterPosition": 0, "Class": true, "BaseTypeOrInterfaces": [] } ] }, { "Kind": "Method", "Name": "AddUserSecrets", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "optional", "Type": "System.Boolean" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [ { "ParameterName": "T", "ParameterPosition": 0, "Class": true, "BaseTypeOrInterfaces": [] } ] }, { "Kind": "Method", "Name": "AddUserSecrets", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "assembly", "Type": "System.Reflection.Assembly" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddUserSecrets", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "assembly", "Type": "System.Reflection.Assembly" }, { "Name": "optional", "Type": "System.Boolean" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddUserSecrets", "Parameters": [ { "Name": "configuration", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "userSecretsId", "Type": "System.String" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.UserSecrets.PathHelper", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "GetSecretsPathFromSecretsId", "Parameters": [ { "Name": "userSecretsId", "Type": "System.String" } ], "ReturnType": "System.String", "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.UserSecrets.UserSecretsIdAttribute", "Visibility": "Public", "Kind": "Class", "BaseType": "System.Attribute", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "get_UserSecretsId", "Parameters": [], "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { "Name": "userSecretId", "Type": "System.String" } ], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] } ] } ================================================ FILE: src/Config.UserSecrets/build/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.props ================================================  ================================================ FILE: src/Config.UserSecrets/build/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.targets ================================================  $(MSBuildAllProjects);$(MSBuildThisFileFullPath) true <_Parameter1>$(UserSecretsId.Trim()) ================================================ FILE: src/Config.Xml/Config.Xml.csproj ================================================  Microsoft.Extensions.Configuration.Xml Microsoft.Extensions.Configuration.Xml XML configuration provider implementation for Microsoft.Extensions.Configuration. netstandard2.0 $(PackageTags);xml ================================================ FILE: src/Config.Xml/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.Extensions.Configuration.Xml.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] ================================================ FILE: src/Config.Xml/Properties/Resources.Designer.cs ================================================ // namespace Microsoft.Extensions.Configuration.Xml { using System.Globalization; using System.Reflection; using System.Resources; internal static class Resources { private static readonly ResourceManager _resourceManager = new ResourceManager("Microsoft.Extensions.Configuration.Xml.Resources", typeof(Resources).GetTypeInfo().Assembly); /// /// File path must be a non-empty string. /// internal static string Error_InvalidFilePath { get => GetString("Error_InvalidFilePath"); } /// /// File path must be a non-empty string. /// internal static string FormatError_InvalidFilePath() => GetString("Error_InvalidFilePath"); /// /// A duplicate key '{0}' was found.{1} /// internal static string Error_KeyIsDuplicated { get => GetString("Error_KeyIsDuplicated"); } /// /// A duplicate key '{0}' was found.{1} /// internal static string FormatError_KeyIsDuplicated(object p0, object p1) => string.Format(CultureInfo.CurrentCulture, GetString("Error_KeyIsDuplicated"), p0, p1); /// /// XML namespaces are not supported.{0} /// internal static string Error_NamespaceIsNotSupported { get => GetString("Error_NamespaceIsNotSupported"); } /// /// XML namespaces are not supported.{0} /// internal static string FormatError_NamespaceIsNotSupported(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("Error_NamespaceIsNotSupported"), p0); /// /// Unsupported node type '{0}' was found.{1} /// internal static string Error_UnsupportedNodeType { get => GetString("Error_UnsupportedNodeType"); } /// /// Unsupported node type '{0}' was found.{1} /// internal static string FormatError_UnsupportedNodeType(object p0, object p1) => string.Format(CultureInfo.CurrentCulture, GetString("Error_UnsupportedNodeType"), p0, p1); /// /// Line {0}, position {1}. /// internal static string Msg_LineInfo { get => GetString("Msg_LineInfo"); } /// /// Line {0}, position {1}. /// internal static string FormatMsg_LineInfo(object p0, object p1) => string.Format(CultureInfo.CurrentCulture, GetString("Msg_LineInfo"), p0, p1); private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); System.Diagnostics.Debug.Assert(value != null); if (formatterNames != null) { for (var i = 0; i < formatterNames.Length; i++) { value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); } } return value; } } } ================================================ FILE: src/Config.Xml/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 File path must be a non-empty string. A duplicate key '{0}' was found.{1} XML namespaces are not supported.{0} Unsupported node type '{0}' was found.{1} Line {0}, position {1}. ================================================ FILE: src/Config.Xml/XmlConfigurationExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using Microsoft.Extensions.Configuration.Xml; using Microsoft.Extensions.FileProviders; namespace Microsoft.Extensions.Configuration { /// /// Extension methods for adding . /// public static class XmlConfigurationExtensions { /// /// Adds the XML configuration provider at to . /// /// The to add to. /// Path relative to the base path stored in /// of . /// The . public static IConfigurationBuilder AddXmlFile(this IConfigurationBuilder builder, string path) { return AddXmlFile(builder, provider: null, path: path, optional: false, reloadOnChange: false); } /// /// Adds the XML configuration provider at to . /// /// The to add to. /// Path relative to the base path stored in /// of . /// Whether the file is optional. /// The . public static IConfigurationBuilder AddXmlFile(this IConfigurationBuilder builder, string path, bool optional) { return AddXmlFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false); } /// /// Adds the XML configuration provider at to . /// /// The to add to. /// Path relative to the base path stored in /// of . /// Whether the file is optional. /// Whether the configuration should be reloaded if the file changes. /// The . public static IConfigurationBuilder AddXmlFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange) { return AddXmlFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange); } /// /// Adds a XML configuration source to . /// /// The to add to. /// The to use to access the file. /// Path relative to the base path stored in /// of . /// Whether the file is optional. /// Whether the configuration should be reloaded if the file changes. /// The . public static IConfigurationBuilder AddXmlFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (string.IsNullOrEmpty(path)) { throw new ArgumentException(Resources.Error_InvalidFilePath, nameof(path)); } return builder.AddXmlFile(s => { s.FileProvider = provider; s.Path = path; s.Optional = optional; s.ReloadOnChange = reloadOnChange; s.ResolveFileProvider(); }); } /// /// Adds a XML configuration source to . /// /// The to add to. /// Configures the source. /// The . public static IConfigurationBuilder AddXmlFile(this IConfigurationBuilder builder, Action configureSource) => builder.Add(configureSource); } } ================================================ FILE: src/Config.Xml/XmlConfigurationProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; namespace Microsoft.Extensions.Configuration.Xml { /// /// Represents an XML file as an . /// public class XmlConfigurationProvider : FileConfigurationProvider { private const string NameAttributeKey = "Name"; /// /// Initializes a new instance with the specified source. /// /// The source settings. public XmlConfigurationProvider(XmlConfigurationSource source) : base(source) { } internal XmlDocumentDecryptor Decryptor { get; set; } = XmlDocumentDecryptor.Instance; /// /// Loads the XML data from a stream. /// /// The stream to read. public override void Load(Stream stream) { var data = new Dictionary(StringComparer.OrdinalIgnoreCase); var readerSettings = new XmlReaderSettings() { CloseInput = false, // caller will close the stream DtdProcessing = DtdProcessing.Prohibit, IgnoreComments = true, IgnoreWhitespace = true }; using (var reader = Decryptor.CreateDecryptingXmlReader(stream, readerSettings)) { var prefixStack = new Stack(); SkipUntilRootElement(reader); // We process the root element individually since it doesn't contribute to prefix ProcessAttributes(reader, prefixStack, data, AddNamePrefix); ProcessAttributes(reader, prefixStack, data, AddAttributePair); var preNodeType = reader.NodeType; while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.Element: prefixStack.Push(reader.LocalName); ProcessAttributes(reader, prefixStack, data, AddNamePrefix); ProcessAttributes(reader, prefixStack, data, AddAttributePair); // If current element is self-closing if (reader.IsEmptyElement) { prefixStack.Pop(); } break; case XmlNodeType.EndElement: if (prefixStack.Any()) { // If this EndElement node comes right after an Element node, // it means there is no text/CDATA node in current element if (preNodeType == XmlNodeType.Element) { var key = ConfigurationPath.Combine(prefixStack.Reverse()); data[key] = string.Empty; } prefixStack.Pop(); } break; case XmlNodeType.CDATA: case XmlNodeType.Text: { var key = ConfigurationPath.Combine(prefixStack.Reverse()); if (data.ContainsKey(key)) { throw new FormatException(Resources.FormatError_KeyIsDuplicated(key, GetLineInfo(reader))); } data[key] = reader.Value; break; } case XmlNodeType.XmlDeclaration: case XmlNodeType.ProcessingInstruction: case XmlNodeType.Comment: case XmlNodeType.Whitespace: // Ignore certain types of nodes break; default: throw new FormatException(Resources.FormatError_UnsupportedNodeType(reader.NodeType, GetLineInfo(reader))); } preNodeType = reader.NodeType; // If this element is a self-closing element, // we pretend that we just processed an EndElement node // because a self-closing element contains an end within itself if (preNodeType == XmlNodeType.Element && reader.IsEmptyElement) { preNodeType = XmlNodeType.EndElement; } } } Data = data; } private void SkipUntilRootElement(XmlReader reader) { while (reader.Read()) { if (reader.NodeType != XmlNodeType.XmlDeclaration && reader.NodeType != XmlNodeType.ProcessingInstruction) { break; } } } private static string GetLineInfo(XmlReader reader) { var lineInfo = reader as IXmlLineInfo; return lineInfo == null ? string.Empty : Resources.FormatMsg_LineInfo(lineInfo.LineNumber, lineInfo.LinePosition); } private void ProcessAttributes(XmlReader reader, Stack prefixStack, IDictionary data, Action, IDictionary, XmlWriter> act, XmlWriter writer = null) { for (int i = 0; i < reader.AttributeCount; i++) { reader.MoveToAttribute(i); // If there is a namespace attached to current attribute if (!string.IsNullOrEmpty(reader.NamespaceURI)) { throw new FormatException(Resources.FormatError_NamespaceIsNotSupported(GetLineInfo(reader))); } act(reader, prefixStack, data, writer); } // Go back to the element containing the attributes we just processed reader.MoveToElement(); } // The special attribute "Name" only contributes to prefix // This method adds a prefix if current node in reader represents a "Name" attribute private static void AddNamePrefix(XmlReader reader, Stack prefixStack, IDictionary data, XmlWriter writer) { if (!string.Equals(reader.LocalName, NameAttributeKey, StringComparison.OrdinalIgnoreCase)) { return; } // If current element is not root element if (prefixStack.Any()) { var lastPrefix = prefixStack.Pop(); prefixStack.Push(ConfigurationPath.Combine(lastPrefix, reader.Value)); } else { prefixStack.Push(reader.Value); } } // Common attributes contribute to key-value pairs // This method adds a key-value pair if current node in reader represents a common attribute private static void AddAttributePair(XmlReader reader, Stack prefixStack, IDictionary data, XmlWriter writer) { prefixStack.Push(reader.LocalName); var key = ConfigurationPath.Combine(prefixStack.Reverse()); if (data.ContainsKey(key)) { throw new FormatException(Resources.FormatError_KeyIsDuplicated(key, GetLineInfo(reader))); } data[key] = reader.Value; prefixStack.Pop(); } } } ================================================ FILE: src/Config.Xml/XmlConfigurationSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Extensions.Configuration.Xml { /// /// An XML file based . /// public class XmlConfigurationSource : FileConfigurationSource { /// /// Builds the for this source. /// /// The . /// A public override IConfigurationProvider Build(IConfigurationBuilder builder) { EnsureDefaults(builder); return new XmlConfigurationProvider(this); } } } ================================================ FILE: src/Config.Xml/XmlDocumentDecryptor.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using System.Security.Cryptography.Xml; using System.Xml; namespace Microsoft.Extensions.Configuration.Xml { /// /// Class responsible for encrypting and decrypting XML. /// public class XmlDocumentDecryptor { /// /// Accesses the singleton decryptor instance. /// public static readonly XmlDocumentDecryptor Instance = new XmlDocumentDecryptor(); private readonly Func _encryptedXmlFactory; /// /// Initializes a XmlDocumentDecryptor. /// // don't create an instance of this directly protected XmlDocumentDecryptor() : this(DefaultEncryptedXmlFactory) { } // for testing only internal XmlDocumentDecryptor(Func encryptedXmlFactory) { _encryptedXmlFactory = encryptedXmlFactory; } private static bool ContainsEncryptedData(XmlDocument document) { // EncryptedXml will simply decrypt the document in-place without telling // us that it did so, so we need to perform a check to see if EncryptedXml // will actually do anything. The below check for an encrypted data blob // is the same one that EncryptedXml would have performed. var namespaceManager = new XmlNamespaceManager(document.NameTable); namespaceManager.AddNamespace("enc", "http://www.w3.org/2001/04/xmlenc#"); return (document.SelectSingleNode("//enc:EncryptedData", namespaceManager) != null); } /// /// Returns an XmlReader that decrypts data transparently. /// public XmlReader CreateDecryptingXmlReader(Stream input, XmlReaderSettings settings) { // XML-based configurations aren't really all that big, so we can buffer // the whole thing in memory while we determine decryption operations. var memStream = new MemoryStream(); input.CopyTo(memStream); memStream.Position = 0; // First, consume the entire XmlReader as an XmlDocument. var document = new XmlDocument(); using (var reader = XmlReader.Create(memStream, settings)) { document.Load(reader); } memStream.Position = 0; if (ContainsEncryptedData(document)) { return DecryptDocumentAndCreateXmlReader(document); } else { // If no decryption would have taken place, return a new fresh reader // based on the memory stream (which doesn't need to be disposed). return XmlReader.Create(memStream, settings); } } /// /// Creates a reader that can decrypt an encrypted XML document. /// /// The document. /// An XmlReader which can read the document. protected virtual XmlReader DecryptDocumentAndCreateXmlReader(XmlDocument document) { // Perform the actual decryption step, updating the XmlDocument in-place. var encryptedXml = _encryptedXmlFactory(document); encryptedXml.DecryptDocument(); // Finally, return the new XmlReader from the updated XmlDocument. // Error messages based on this XmlReader won't show line numbers, // but that's fine since we transformed the document anyway. return document.CreateNavigator().ReadSubtree(); } private static EncryptedXml DefaultEncryptedXmlFactory(XmlDocument document) => new EncryptedXml(document); } } ================================================ FILE: src/Config.Xml/baseline.netcore.json ================================================ { "AssemblyIdentity": "Microsoft.Extensions.Configuration.Xml, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.Configuration.XmlConfigurationExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "AddXmlFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "path", "Type": "System.String" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddXmlFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "path", "Type": "System.String" }, { "Name": "optional", "Type": "System.Boolean" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddXmlFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "path", "Type": "System.String" }, { "Name": "optional", "Type": "System.Boolean" }, { "Name": "reloadOnChange", "Type": "System.Boolean" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddXmlFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "provider", "Type": "Microsoft.Extensions.FileProviders.IFileProvider" }, { "Name": "path", "Type": "System.String" }, { "Name": "optional", "Type": "System.Boolean" }, { "Name": "reloadOnChange", "Type": "System.Boolean" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "AddXmlFile", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" }, { "Name": "configureSource", "Type": "System.Action" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.Xml.XmlConfigurationProvider", "Visibility": "Public", "Kind": "Class", "BaseType": "Microsoft.Extensions.Configuration.FileConfigurationProvider", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Load", "Parameters": [ { "Name": "stream", "Type": "System.IO.Stream" } ], "ReturnType": "System.Void", "Virtual": true, "Override": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { "Name": "source", "Type": "Microsoft.Extensions.Configuration.Xml.XmlConfigurationSource" } ], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.Xml.XmlConfigurationSource", "Visibility": "Public", "Kind": "Class", "BaseType": "Microsoft.Extensions.Configuration.FileConfigurationSource", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "Build", "Parameters": [ { "Name": "builder", "Type": "Microsoft.Extensions.Configuration.IConfigurationBuilder" } ], "ReturnType": "Microsoft.Extensions.Configuration.IConfigurationProvider", "Virtual": true, "Override": true, "ImplementedInterface": "Microsoft.Extensions.Configuration.IConfigurationSource", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { "Name": "Microsoft.Extensions.Configuration.Xml.XmlDocumentDecryptor", "Visibility": "Public", "Kind": "Class", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", "Name": "CreateDecryptingXmlReader", "Parameters": [ { "Name": "input", "Type": "System.IO.Stream" }, { "Name": "settings", "Type": "System.Xml.XmlReaderSettings" } ], "ReturnType": "System.Xml.XmlReader", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", "Name": "DecryptDocumentAndCreateXmlReader", "Parameters": [ { "Name": "document", "Type": "System.Xml.XmlDocument" } ], "ReturnType": "System.Xml.XmlReader", "Virtual": true, "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [], "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Field", "Name": "Instance", "Parameters": [], "ReturnType": "Microsoft.Extensions.Configuration.Xml.XmlDocumentDecryptor", "Static": true, "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] } ] } ================================================ FILE: src/Directory.Build.props ================================================ true configuration ================================================ FILE: test/Config.AzureKeyVault.Test/AzureKeyVaultConfigurationTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Azure.KeyVault; using Microsoft.Azure.KeyVault.Models; using Microsoft.Extensions.Configuration.Test; using Microsoft.Rest.Azure; using Moq; using Xunit; namespace Microsoft.Extensions.Configuration.AzureKeyVault.Test { public class AzureKeyVaultConfigurationTest { private const string VaultUri = "https://vault"; [Fact] public void LoadsAllSecretsFromVault() { var client = new Mock(MockBehavior.Strict); var secret1Id = GetSecretId("Secret1"); var secret2Id = GetSecretId("Secret2"); client.Setup(c => c.GetSecretsAsync(VaultUri)).ReturnsAsync(new PageMock() { NextPageLink = "next", Value = new[] { new SecretItem { Id = secret1Id, Attributes = new SecretAttributes { Enabled = true } } } }); client.Setup(c => c.GetSecretsNextAsync("next")).ReturnsAsync(new PageMock() { Value = new[] { new SecretItem { Id = secret2Id, Attributes = new SecretAttributes { Enabled = true } } } }); client.Setup(c => c.GetSecretAsync(secret1Id)).ReturnsAsync(new SecretBundle() { Value = "Value1", Id = secret1Id }); client.Setup(c => c.GetSecretAsync(secret2Id)).ReturnsAsync(new SecretBundle() { Value = "Value2", Id = secret2Id }); // Act var provider = new AzureKeyVaultConfigurationProvider(client.Object, VaultUri, new DefaultKeyVaultSecretManager()); provider.Load(); // Assert client.VerifyAll(); var childKeys = provider.GetChildKeys(Enumerable.Empty(), null).ToArray(); Assert.Equal(new[] { "Secret1", "Secret2" }, childKeys); Assert.Equal("Value1", provider.Get("Secret1")); Assert.Equal("Value2", provider.Get("Secret2")); } [Fact] public void DoesNotLoadFilteredItems() { var client = new Mock(MockBehavior.Strict); var secret1Id = GetSecretId("Secret1"); var secret2Id = GetSecretId("Secret2"); client.Setup(c => c.GetSecretsAsync(VaultUri)).ReturnsAsync(new PageMock() { Value = new[] { new SecretItem { Id = secret1Id, Attributes = new SecretAttributes { Enabled = true } }, new SecretItem { Id = secret2Id, Attributes = new SecretAttributes { Enabled = true } } } }); client.Setup(c => c.GetSecretAsync(secret1Id)).ReturnsAsync(new SecretBundle() { Value = "Value1", Id = secret1Id }); // Act var provider = new AzureKeyVaultConfigurationProvider(client.Object, VaultUri, new EndsWithOneKeyVaultSecretManager()); provider.Load(); // Assert client.VerifyAll(); var childKeys = provider.GetChildKeys(Enumerable.Empty(), null).ToArray(); Assert.Equal(new[] { "Secret1" }, childKeys); Assert.Equal("Value1", provider.Get("Secret1")); } [Fact] public void DoesNotLoadDisabledItems() { var client = new Mock(MockBehavior.Strict); var secret1Id = GetSecretId("Secret1"); var secret2Id = GetSecretId("Secret2"); var secret3Id = GetSecretId("Secret3"); var secret4Id = GetSecretId("Secret4"); client.Setup(c => c.GetSecretsAsync(VaultUri)).ReturnsAsync(new PageMock() { NextPageLink = "next", Value = new[] { new SecretItem { Id = secret1Id, Attributes = new SecretAttributes { Enabled = true } } } }); client.Setup(c => c.GetSecretsNextAsync("next")).ReturnsAsync(new PageMock() { Value = new[] { new SecretItem { Id = secret2Id, Attributes = new SecretAttributes { Enabled = false } }, new SecretItem { Id = secret3Id, Attributes = new SecretAttributes { Enabled = null } }, new SecretItem { Id = secret4Id, Attributes = null }, } }); client.Setup(c => c.GetSecretAsync(secret1Id)).ReturnsAsync(new SecretBundle() { Value = "Value1", Id = secret1Id }); // Act var provider = new AzureKeyVaultConfigurationProvider(client.Object, VaultUri, new DefaultKeyVaultSecretManager()); provider.Load(); // Assert client.VerifyAll(); var childKeys = provider.GetChildKeys(Enumerable.Empty(), null).ToArray(); Assert.Equal(new[] { "Secret1" }, childKeys); Assert.Equal("Value1", provider.Get("Secret1")); Assert.Throws(() => provider.Get("Secret2")); Assert.Throws(() => provider.Get("Secret3")); Assert.Throws(() => provider.Get("Secret4")); } [Fact] public void SupportsReload() { var client = new Mock(MockBehavior.Strict); var secret1Id = GetSecretId("Secret1"); var value = "Value1"; client.Setup(c => c.GetSecretsAsync(VaultUri)).ReturnsAsync(new PageMock() { Value = new[] { new SecretItem { Id = secret1Id, Attributes = new SecretAttributes { Enabled = true } } } }); client.Setup(c => c.GetSecretAsync(secret1Id)).Returns((string id) => Task.FromResult(new SecretBundle() { Value = value, Id = id })); // Act & Assert var provider = new AzureKeyVaultConfigurationProvider(client.Object, VaultUri, new DefaultKeyVaultSecretManager()); provider.Load(); client.VerifyAll(); Assert.Equal("Value1", provider.Get("Secret1")); value = "Value2"; provider.Load(); Assert.Equal("Value2", provider.Get("Secret1")); } [Fact] public void ReplaceDoubleMinusInKeyName() { var client = new Mock(MockBehavior.Strict); var secret1Id = GetSecretId("Section--Secret1"); client.Setup(c => c.GetSecretsAsync(VaultUri)).ReturnsAsync(new PageMock() { Value = new[] { new SecretItem { Id = secret1Id, Attributes = new SecretAttributes { Enabled = true } } } }); client.Setup(c => c.GetSecretAsync(secret1Id)).ReturnsAsync(new SecretBundle() { Value = "Value1", Id = secret1Id }); // Act var provider = new AzureKeyVaultConfigurationProvider(client.Object, VaultUri, new DefaultKeyVaultSecretManager()); provider.Load(); // Assert client.VerifyAll(); Assert.Equal("Value1", provider.Get("Section:Secret1")); } [Fact] public void ConstructorThrowsForNullManager() { Assert.Throws(() => new AzureKeyVaultConfigurationProvider(Mock.Of(), VaultUri, null)); } private string GetSecretId(string name) => new SecretIdentifier(VaultUri, name).Identifier; private class EndsWithOneKeyVaultSecretManager : DefaultKeyVaultSecretManager { public override bool Load(SecretItem secret) { return secret.Identifier.Name.EndsWith("1"); } } private class PageMock: IPage { public IEnumerable Value { get; set; } public IEnumerator GetEnumerator() { return Value.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public string NextPageLink { get; set; } } } } ================================================ FILE: test/Config.AzureKeyVault.Test/Config.AzureKeyVault.Test.csproj ================================================  Microsoft.Extensions.Configuration.AzureKeyVault.Test Microsoft.Extensions.Configuration.AzureKeyVault.Test net461 ================================================ FILE: test/Config.Binder.Test/Config.Binder.Test.csproj ================================================ Microsoft.Extensions.Configuration.Binder.Test Microsoft.Extensions.Configuration.Binder.Test netcoreapp2.2;net461 netcoreapp2.2 ================================================ FILE: test/Config.Binder.Test/ConfigurationBinderTests.cs ================================================ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Reflection; using Xunit; namespace Microsoft.Extensions.Configuration.Binder.Test { public class ConfigurationBinderTests { public class ComplexOptions { public ComplexOptions() { Nested = new NestedOptions(); Virtual = "complex"; } public NestedOptions Nested { get; set; } public int Integer { get; set; } public bool Boolean { get; set; } public virtual string Virtual { get; set; } public object Object { get; set; } public string PrivateSetter { get; private set; } public string ProtectedSetter { get; protected set; } public string InternalSetter { get; internal set; } public static string StaticProperty { get; set; } private string PrivateProperty { get; set; } internal string InternalProperty { get; set; } protected string ProtectedProperty { get; set; } protected string ProtectedPrivateSet { get; private set; } private string PrivateReadOnly { get; } internal string InternalReadOnly { get; } protected string ProtectedReadOnly { get; } public string ReadOnly { get { return null; } } } public class NestedOptions { public int Integer { get; set; } } public class DerivedOptions : ComplexOptions { public override string Virtual { get { return base.Virtual; } set { base.Virtual = "Derived:" + value; } } } public class NullableOptions { public bool? MyNullableBool { get; set; } public int? MyNullableInt { get; set; } public DateTime? MyNullableDateTime { get; set; } } public class EnumOptions { public UriKind UriKind { get; set; } } public class GenericOptions { public T Value { get; set; } } public class ConfigurationInterfaceOptions { public IConfigurationSection Section { get; set; } } public class DerivedOptionsWithIConfigurationSection : DerivedOptions { public IConfigurationSection DerivedSection { get; set; } } [Fact] public void CanBindIConfigurationSection() { var dic = new Dictionary { {"Section:Integer", "-2"}, {"Section:Boolean", "TRUe"}, {"Section:Nested:Integer", "11"}, {"Section:Virtual", "Sup"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = config.Get(); var childOptions = options.Section.Get(); Assert.True(childOptions.Boolean); Assert.Equal(-2, childOptions.Integer); Assert.Equal(11, childOptions.Nested.Integer); Assert.Equal("Derived:Sup", childOptions.Virtual); Assert.Equal("Section", options.Section.Key); Assert.Equal("Section", options.Section.Path); Assert.Null(options.Section.Value); } [Fact] public void CanBindWithKeyOverload() { var dic = new Dictionary { {"Section:Integer", "-2"}, {"Section:Boolean", "TRUe"}, {"Section:Nested:Integer", "11"}, {"Section:Virtual", "Sup"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = new DerivedOptions(); config.Bind("Section", options); Assert.True(options.Boolean); Assert.Equal(-2, options.Integer); Assert.Equal(11, options.Nested.Integer); Assert.Equal("Derived:Sup", options.Virtual); } [Fact] public void CanBindIConfigurationSectionWithDerivedOptionsSection() { var dic = new Dictionary { {"Section:Integer", "-2"}, {"Section:Boolean", "TRUe"}, {"Section:Nested:Integer", "11"}, {"Section:Virtual", "Sup"}, {"Section:DerivedSection:Nested:Integer", "11"}, {"Section:DerivedSection:Virtual", "Sup"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = config.Get(); var childOptions = options.Section.Get(); var childDerivedOptions = childOptions.DerivedSection.Get(); Assert.True(childOptions.Boolean); Assert.Equal(-2, childOptions.Integer); Assert.Equal(11, childOptions.Nested.Integer); Assert.Equal("Derived:Sup", childOptions.Virtual); Assert.Equal(11, childDerivedOptions.Nested.Integer); Assert.Equal("Derived:Sup", childDerivedOptions.Virtual); Assert.Equal("Section", options.Section.Key); Assert.Equal("Section", options.Section.Path); Assert.Equal("DerivedSection", childOptions.DerivedSection.Key); Assert.Equal("Section:DerivedSection", childOptions.DerivedSection.Path); Assert.Null(options.Section.Value); } [Fact] public void EmptyStringIsNullable() { var dic = new Dictionary { {"empty", ""}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); Assert.Null(config.GetValue("empty")); Assert.Null(config.GetValue("empty")); } [Fact] public void GetScalarNullable() { var dic = new Dictionary { {"Integer", "-2"}, {"Boolean", "TRUe"}, {"Nested:Integer", "11"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); Assert.True(config.GetValue("Boolean")); Assert.Equal(-2, config.GetValue("Integer")); Assert.Equal(11, config.GetValue("Nested:Integer")); } [Fact] public void CanBindToObjectProperty() { var dic = new Dictionary { {"Object", "whatever" } }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = new ComplexOptions(); config.Bind(options); Assert.Equal("whatever", options.Object); } [Fact] public void GetNullValue() { var dic = new Dictionary { {"Integer", null}, {"Boolean", null}, {"Nested:Integer", null}, {"Object", null } }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); Assert.False(config.GetValue("Boolean")); Assert.Equal(0, config.GetValue("Integer")); Assert.Equal(0, config.GetValue("Nested:Integer")); Assert.Null(config.GetValue("Object")); Assert.False(config.GetSection("Boolean").Get()); Assert.Equal(0, config.GetSection("Integer").Get()); Assert.Equal(0, config.GetSection("Nested:Integer").Get()); Assert.Null(config.GetSection("Object").Get()); } [Fact] public void GetDefaultsWhenDataDoesNotExist() { var dic = new Dictionary { }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); Assert.False(config.GetValue("Boolean")); Assert.Equal(0, config.GetValue("Integer")); Assert.Equal(0, config.GetValue("Nested:Integer")); Assert.Null(config.GetValue("Object")); Assert.True(config.GetValue("Boolean", true)); Assert.Equal(3, config.GetValue("Integer", 3)); Assert.Equal(1, config.GetValue("Nested:Integer", 1)); var foo = new ComplexOptions(); Assert.Same(config.GetValue("Object", foo), foo); } [Fact] public void GetUri() { var dic = new Dictionary { {"AnUri", "http://www.bing.com"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var uri = config.GetValue("AnUri"); Assert.Equal("http://www.bing.com", uri.OriginalString); } [Theory] [InlineData("2147483647", typeof(int))] [InlineData("4294967295", typeof(uint))] [InlineData("32767", typeof(short))] [InlineData("65535", typeof(ushort))] [InlineData("-9223372036854775808", typeof(long))] [InlineData("18446744073709551615", typeof(ulong))] [InlineData("trUE", typeof(bool))] [InlineData("255", typeof(byte))] [InlineData("127", typeof(sbyte))] [InlineData("\uffff", typeof(char))] [InlineData("79228162514264337593543950335", typeof(decimal))] [InlineData("1.79769e+308", typeof(double))] [InlineData("3.40282347E+38", typeof(float))] [InlineData("2015-12-24T07:34:42-5:00", typeof(DateTime))] [InlineData("12/24/2015 13:44:55 +4", typeof(DateTimeOffset))] [InlineData("99.22:22:22.1234567", typeof(TimeSpan))] [InlineData("http://www.bing.com", typeof(Uri))] // enum test [InlineData("Constructor", typeof(AttributeTargets))] [InlineData("CA761232-ED42-11CE-BACD-00AA0057B223", typeof(Guid))] public void CanReadAllSupportedTypes(string value, Type type) { // arrange var dic = new Dictionary { {"Value", value} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var optionsType = typeof(GenericOptions<>).MakeGenericType(type); var options = Activator.CreateInstance(optionsType); var expectedValue = TypeDescriptor.GetConverter(type).ConvertFromInvariantString(value); // act config.Bind(options); var optionsValue = options.GetType().GetProperty("Value").GetValue(options); var getValueValue = config.GetValue(type, "Value"); var getValue = config.GetSection("Value").Get(type); // assert Assert.Equal(expectedValue, optionsValue); Assert.Equal(expectedValue, getValue); Assert.Equal(expectedValue, getValueValue); } [Theory] [InlineData(typeof(int))] [InlineData(typeof(uint))] [InlineData(typeof(short))] [InlineData(typeof(ushort))] [InlineData(typeof(long))] [InlineData(typeof(ulong))] [InlineData(typeof(bool))] [InlineData(typeof(byte))] [InlineData(typeof(sbyte))] [InlineData(typeof(char))] [InlineData(typeof(decimal))] [InlineData(typeof(double))] [InlineData(typeof(float))] [InlineData(typeof(DateTime))] [InlineData(typeof(DateTimeOffset))] [InlineData(typeof(TimeSpan))] [InlineData(typeof(AttributeTargets))] [InlineData(typeof(Guid))] public void ConsistentExceptionOnFailedBinding(Type type) { // arrange const string IncorrectValue = "Invalid data"; var dic = new Dictionary { {"Value", IncorrectValue} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var optionsType = typeof(GenericOptions<>).MakeGenericType(type); var options = Activator.CreateInstance(optionsType); // act var exception = Assert.Throws( () => config.Bind(options)); var getValueException = Assert.Throws( () => config.GetValue(type, "Value")); var getException = Assert.Throws( () => config.GetSection("Value").Get(type)); // assert Assert.NotNull(exception.InnerException); Assert.NotNull(getException.InnerException); Assert.Equal( Resources.FormatError_FailedBinding(IncorrectValue, type), exception.Message); Assert.Equal( Resources.FormatError_FailedBinding(IncorrectValue, type), getException.Message); Assert.Equal( Resources.FormatError_FailedBinding(IncorrectValue, type), getValueException.Message); } [Fact] public void BinderIgnoresIndexerProperties() { var configurationBuilder = new ConfigurationBuilder(); var config = configurationBuilder.Build(); config.Bind(new List()); } [Fact] public void BindCanReadComplexProperties() { var dic = new Dictionary { {"Integer", "-2"}, {"Boolean", "TRUe"}, {"Nested:Integer", "11"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var instance = new ComplexOptions(); config.Bind(instance); Assert.True(instance.Boolean); Assert.Equal(-2, instance.Integer); Assert.Equal(11, instance.Nested.Integer); } [Fact] public void GetCanReadComplexProperties() { var dic = new Dictionary { {"Integer", "-2"}, {"Boolean", "TRUe"}, {"Nested:Integer", "11"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = new ComplexOptions(); config.Bind(options); Assert.True(options.Boolean); Assert.Equal(-2, options.Integer); Assert.Equal(11, options.Nested.Integer); } [Fact] public void BindCanReadInheritedProperties() { var dic = new Dictionary { {"Integer", "-2"}, {"Boolean", "TRUe"}, {"Nested:Integer", "11"}, {"Virtual", "Sup"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var instance = new DerivedOptions(); config.Bind(instance); Assert.True(instance.Boolean); Assert.Equal(-2, instance.Integer); Assert.Equal(11, instance.Nested.Integer); Assert.Equal("Derived:Sup", instance.Virtual); } [Fact] public void GetCanReadInheritedProperties() { var dic = new Dictionary { {"Integer", "-2"}, {"Boolean", "TRUe"}, {"Nested:Integer", "11"}, {"Virtual", "Sup"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = new DerivedOptions(); config.Bind(options); Assert.True(options.Boolean); Assert.Equal(-2, options.Integer); Assert.Equal(11, options.Nested.Integer); Assert.Equal("Derived:Sup", options.Virtual); } [Fact] public void GetCanReadStaticProperty() { var dic = new Dictionary { {"StaticProperty", "stuff"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = new ComplexOptions(); config.Bind(options); Assert.Equal("stuff", ComplexOptions.StaticProperty); } [Fact] public void BindCanReadStaticProperty() { var dic = new Dictionary { {"StaticProperty", "other stuff"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var instance = new ComplexOptions(); config.Bind(instance); Assert.Equal("other stuff", ComplexOptions.StaticProperty); } [Fact] public void CanGetComplexOptionsWhichHasAlsoHasValue() { var dic = new Dictionary { {"obj", "whut" }, {"obj:Integer", "-2"}, {"obj:Boolean", "TRUe"}, {"obj:Nested:Integer", "11"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = config.GetSection("obj").Get(); Assert.NotNull(options); Assert.True(options.Boolean); Assert.Equal(-2, options.Integer); Assert.Equal(11, options.Nested.Integer); } [Theory] [InlineData("ReadOnly")] [InlineData("PrivateSetter")] [InlineData("ProtectedSetter")] [InlineData("InternalSetter")] [InlineData("InternalProperty")] [InlineData("PrivateProperty")] [InlineData("ProtectedProperty")] [InlineData("ProtectedPrivateSet")] public void GetIgnoresTests(string property) { var dic = new Dictionary { {property, "stuff"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = config.Get(); Assert.Null(options.GetType().GetTypeInfo().GetDeclaredProperty(property).GetValue(options)); } [Theory] [InlineData("PrivateSetter")] [InlineData("ProtectedSetter")] [InlineData("InternalSetter")] [InlineData("InternalProperty")] [InlineData("PrivateProperty")] [InlineData("ProtectedProperty")] [InlineData("ProtectedPrivateSet")] public void GetCanSetNonPublicWhenSet(string property) { var dic = new Dictionary { {property, "stuff"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = config.Get(o => o.BindNonPublicProperties = true); Assert.Equal("stuff", options.GetType().GetTypeInfo().GetDeclaredProperty(property).GetValue(options)); } [Theory] [InlineData("InternalReadOnly")] [InlineData("PrivateReadOnly")] [InlineData("ProtectedReadOnly")] public void NonPublicModeGetStillIgnoresReadonly(string property) { var dic = new Dictionary { {property, "stuff"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = config.Get(o => o.BindNonPublicProperties = true); Assert.Null(options.GetType().GetTypeInfo().GetDeclaredProperty(property).GetValue(options)); } [Theory] [InlineData("ReadOnly")] [InlineData("PrivateSetter")] [InlineData("ProtectedSetter")] [InlineData("InternalSetter")] [InlineData("InternalProperty")] [InlineData("PrivateProperty")] [InlineData("ProtectedProperty")] [InlineData("ProtectedPrivateSet")] public void BindIgnoresTests(string property) { var dic = new Dictionary { {property, "stuff"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = new ComplexOptions(); config.Bind(options); Assert.Null(options.GetType().GetTypeInfo().GetDeclaredProperty(property).GetValue(options)); } [Theory] [InlineData("PrivateSetter")] [InlineData("ProtectedSetter")] [InlineData("InternalSetter")] [InlineData("InternalProperty")] [InlineData("PrivateProperty")] [InlineData("ProtectedProperty")] [InlineData("ProtectedPrivateSet")] public void BindCanSetNonPublicWhenSet(string property) { var dic = new Dictionary { {property, "stuff"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = new ComplexOptions(); config.Bind(options, o => o.BindNonPublicProperties = true ); Assert.Equal("stuff", options.GetType().GetTypeInfo().GetDeclaredProperty(property).GetValue(options)); } [Theory] [InlineData("InternalReadOnly")] [InlineData("PrivateReadOnly")] [InlineData("ProtectedReadOnly")] public void NonPublicModeBindStillIgnoresReadonly(string property) { var dic = new Dictionary { {property, "stuff"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); var options = new ComplexOptions(); config.Bind(options, o => o.BindNonPublicProperties = true); Assert.Null(options.GetType().GetTypeInfo().GetDeclaredProperty(property).GetValue(options)); } [Fact] public void ExceptionWhenTryingToBindToInterface() { var input = new Dictionary { {"ISomeInterfaceProperty:Subkey", "x"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var exception = Assert.Throws( () => config.Bind(new TestOptions())); Assert.Equal( Resources.FormatError_CannotActivateAbstractOrInterface(typeof(ISomeInterface)), exception.Message); } [Fact] public void ExceptionWhenTryingToBindClassWithoutParameterlessConstructor() { var input = new Dictionary { {"ClassWithoutPublicConstructorProperty:Subkey", "x"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var exception = Assert.Throws( () => config.Bind(new TestOptions())); Assert.Equal( Resources.FormatError_MissingParameterlessConstructor(typeof(ClassWithoutPublicConstructor)), exception.Message); } [Fact] public void ExceptionWhenTryingToBindToTypeThrowsWhenActivated() { var input = new Dictionary { {"ThrowsWhenActivatedProperty:subkey", "x"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var exception = Assert.Throws( () => config.Bind(new TestOptions())); Assert.NotNull(exception.InnerException); Assert.Equal( Resources.FormatError_FailedToActivate(typeof(ThrowsWhenActivated)), exception.Message); } [Fact] public void ExceptionIncludesKeyOfFailedBinding() { var input = new Dictionary { {"NestedOptionsProperty:NestedOptions2Property:ISomeInterfaceProperty:subkey", "x"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var exception = Assert.Throws( () => config.Bind(new TestOptions())); Assert.Equal( Resources.FormatError_CannotActivateAbstractOrInterface(typeof(ISomeInterface)), exception.Message); } private interface ISomeInterface { } private class ClassWithoutPublicConstructor { private ClassWithoutPublicConstructor() { } } private class ThrowsWhenActivated { public ThrowsWhenActivated() { throw new Exception(); } } private class NestedOptions1 { public NestedOptions2 NestedOptions2Property { get; set; } } private class NestedOptions2 { public ISomeInterface ISomeInterfaceProperty { get; set; } } private class TestOptions { public ISomeInterface ISomeInterfaceProperty { get; set; } public ClassWithoutPublicConstructor ClassWithoutPublicConstructorProperty { get; set; } public int IntProperty { get; set; } public ThrowsWhenActivated ThrowsWhenActivatedProperty { get; set; } public NestedOptions1 NestedOptionsProperty { get; set; } } } } ================================================ FILE: test/Config.Binder.Test/ConfigurationCollectionBindingTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using Xunit; namespace Microsoft.Extensions.Configuration.Binder.Test { public class ConfigurationCollectionBinding { [Fact] public void GetList() { var input = new Dictionary { {"StringList:0", "val0"}, {"StringList:1", "val1"}, {"StringList:2", "val2"}, {"StringList:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var list = new List(); config.GetSection("StringList").Bind(list); Assert.Equal(4, list.Count); Assert.Equal("val0", list[0]); Assert.Equal("val1", list[1]); Assert.Equal("val2", list[2]); Assert.Equal("valx", list[3]); } [Fact] public void GetListNullValues() { var input = new Dictionary { {"StringList:0", null}, {"StringList:1", null}, {"StringList:2", null}, {"StringList:x", null} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var list = new List(); config.GetSection("StringList").Bind(list); Assert.Empty(list); } [Fact] public void GetListInvalidValues() { var input = new Dictionary { {"InvalidList:0", "true"}, {"InvalidList:1", "invalid"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var list = new List(); config.GetSection("InvalidList").Bind(list); Assert.Single(list); Assert.True(list[0]); } [Fact] public void BindList() { var input = new Dictionary { {"StringList:0", "val0"}, {"StringList:1", "val1"}, {"StringList:2", "val2"}, {"StringList:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var list = new List(); config.GetSection("StringList").Bind(list); Assert.Equal(4, list.Count); Assert.Equal("val0", list[0]); Assert.Equal("val1", list[1]); Assert.Equal("val2", list[2]); Assert.Equal("valx", list[3]); } [Fact] public void GetObjectList() { var input = new Dictionary { {"ObjectList:0:Integer", "30"}, {"ObjectList:1:Integer", "31"}, {"ObjectList:2:Integer", "32"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new List(); config.GetSection("ObjectList").Bind(options); Assert.Equal(3, options.Count); Assert.Equal(30, options[0].Integer); Assert.Equal(31, options[1].Integer); Assert.Equal(32, options[2].Integer); } [Fact] public void GetStringDictionary() { var input = new Dictionary { {"StringDictionary:abc", "val_1"}, {"StringDictionary:def", "val_2"}, {"StringDictionary:ghi", "val_3"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new Dictionary(); config.GetSection("StringDictionary").Bind(options); Assert.Equal(3, options.Count); Assert.Equal("val_1", options["abc"]); Assert.Equal("val_2", options["def"]); Assert.Equal("val_3", options["ghi"]); } [Fact] public void GetEnumDictionary() { var input = new Dictionary { {"EnumDictionary:abc", "val_1"}, {"EnumDictionary:def", "val_2"}, {"EnumDictionary:ghi", "val_3"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new Dictionary(); config.GetSection("EnumDictionary").Bind(options); Assert.Equal(3, options.Count); Assert.Equal("val_1", options[KeyEnum.abc]); Assert.Equal("val_2", options[KeyEnum.def]); Assert.Equal("val_3", options[KeyEnum.ghi]); } [Fact] public void GetStringList() { var input = new Dictionary { {"StringList:0", "val0"}, {"StringList:1", "val1"}, {"StringList:2", "val2"}, {"StringList:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithLists(); config.Bind(options); var list = options.StringList; Assert.Equal(4, list.Count); Assert.Equal("val0", list[0]); Assert.Equal("val1", list[1]); Assert.Equal("val2", list[2]); Assert.Equal("valx", list[3]); } [Fact] public void BindStringList() { var input = new Dictionary { {"StringList:0", "val0"}, {"StringList:1", "val1"}, {"StringList:2", "val2"}, {"StringList:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithLists(); config.Bind(options); var list = options.StringList; Assert.Equal(4, list.Count); Assert.Equal("val0", list[0]); Assert.Equal("val1", list[1]); Assert.Equal("val2", list[2]); Assert.Equal("valx", list[3]); } [Fact] public void GetIntList() { var input = new Dictionary { {"IntList:0", "42"}, {"IntList:1", "43"}, {"IntList:2", "44"}, {"IntList:x", "45"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithLists(); config.Bind(options); var list = options.IntList; Assert.Equal(4, list.Count); Assert.Equal(42, list[0]); Assert.Equal(43, list[1]); Assert.Equal(44, list[2]); Assert.Equal(45, list[3]); } [Fact] public void BindIntList() { var input = new Dictionary { {"IntList:0", "42"}, {"IntList:1", "43"}, {"IntList:2", "44"}, {"IntList:x", "45"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithLists(); config.Bind(options); var list = options.IntList; Assert.Equal(4, list.Count); Assert.Equal(42, list[0]); Assert.Equal(43, list[1]); Assert.Equal(44, list[2]); Assert.Equal(45, list[3]); } [Fact] public void AlreadyInitializedListBinding() { var input = new Dictionary { {"AlreadyInitializedList:0", "val0"}, {"AlreadyInitializedList:1", "val1"}, {"AlreadyInitializedList:2", "val2"}, {"AlreadyInitializedList:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithLists(); config.Bind(options); var list = options.AlreadyInitializedList; Assert.Equal(5, list.Count); Assert.Equal("This was here before", list[0]); Assert.Equal("val0", list[1]); Assert.Equal("val1", list[2]); Assert.Equal("val2", list[3]); Assert.Equal("valx", list[4]); } [Fact] public void AlreadyInitializedListInterfaceBinding() { var input = new Dictionary { {"AlreadyInitializedListInterface:0", "val0"}, {"AlreadyInitializedListInterface:1", "val1"}, {"AlreadyInitializedListInterface:2", "val2"}, {"AlreadyInitializedListInterface:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithLists(); config.Bind(options); var list = options.AlreadyInitializedListInterface; Assert.Equal(5, list.Count); Assert.Equal("This was here too", list[0]); Assert.Equal("val0", list[1]); Assert.Equal("val1", list[2]); Assert.Equal("val2", list[3]); Assert.Equal("valx", list[4]); } [Fact] public void CustomListBinding() { var input = new Dictionary { {"CustomList:0", "val0"}, {"CustomList:1", "val1"}, {"CustomList:2", "val2"}, {"CustomList:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithLists(); config.Bind(options); var list = options.CustomList; Assert.Equal(4, list.Count); Assert.Equal("val0", list[0]); Assert.Equal("val1", list[1]); Assert.Equal("val2", list[2]); Assert.Equal("valx", list[3]); } [Fact] public void ObjectListBinding() { var input = new Dictionary { {"ObjectList:0:Integer", "30"}, {"ObjectList:1:Integer", "31"}, {"ObjectList:2:Integer", "32"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithLists(); config.Bind(options); Assert.Equal(3, options.ObjectList.Count); Assert.Equal(30, options.ObjectList[0].Integer); Assert.Equal(31, options.ObjectList[1].Integer); Assert.Equal(32, options.ObjectList[2].Integer); } [Fact] public void NestedListsBinding() { var input = new Dictionary { {"NestedLists:0:0", "val00"}, {"NestedLists:0:1", "val01"}, {"NestedLists:1:0", "val10"}, {"NestedLists:1:1", "val11"}, {"NestedLists:1:2", "val12"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithLists(); config.Bind(options); Assert.Equal(2, options.NestedLists.Count); Assert.Equal(2, options.NestedLists[0].Count); Assert.Equal(3, options.NestedLists[1].Count); Assert.Equal("val00", options.NestedLists[0][0]); Assert.Equal("val01", options.NestedLists[0][1]); Assert.Equal("val10", options.NestedLists[1][0]); Assert.Equal("val11", options.NestedLists[1][1]); Assert.Equal("val12", options.NestedLists[1][2]); } [Fact] public void StringDictionaryBinding() { var input = new Dictionary { {"StringDictionary:abc", "val_1"}, {"StringDictionary:def", "val_2"}, {"StringDictionary:ghi", "val_3"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithDictionary(); config.Bind(options); Assert.Equal(3, options.StringDictionary.Count); Assert.Equal("val_1", options.StringDictionary["abc"]); Assert.Equal("val_2", options.StringDictionary["def"]); Assert.Equal("val_3", options.StringDictionary["ghi"]); } [Fact] public void AlreadyInitializedStringDictionaryBinding() { var input = new Dictionary { {"AlreadyInitializedStringDictionaryInterface:abc", "val_1"}, {"AlreadyInitializedStringDictionaryInterface:def", "val_2"}, {"AlreadyInitializedStringDictionaryInterface:ghi", "val_3"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithDictionary(); config.Bind(options); Assert.NotNull(options.AlreadyInitializedStringDictionaryInterface); Assert.Equal(4, options.AlreadyInitializedStringDictionaryInterface.Count); Assert.Equal("This was already here", options.AlreadyInitializedStringDictionaryInterface["123"]); Assert.Equal("val_1", options.AlreadyInitializedStringDictionaryInterface["abc"]); Assert.Equal("val_2", options.AlreadyInitializedStringDictionaryInterface["def"]); Assert.Equal("val_3", options.AlreadyInitializedStringDictionaryInterface["ghi"]); } [Fact] public void CanOverrideExistingDictionaryKey() { var input = new Dictionary { {"abc", "override"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new Dictionary { {"abc", "default"} }; config.Bind(options); var optionsCount = options.Count; Assert.Equal(1, optionsCount); Assert.Equal("override", options["abc"]); } [Fact] public void IntDictionaryBinding() { var input = new Dictionary { {"IntDictionary:abc", "42"}, {"IntDictionary:def", "43"}, {"IntDictionary:ghi", "44"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithDictionary(); config.Bind(options); Assert.Equal(3, options.IntDictionary.Count); Assert.Equal(42, options.IntDictionary["abc"]); Assert.Equal(43, options.IntDictionary["def"]); Assert.Equal(44, options.IntDictionary["ghi"]); } [Fact] public void ObjectDictionary() { var input = new Dictionary { {"ObjectDictionary:abc:Integer", "1"}, {"ObjectDictionary:def:Integer", "2"}, {"ObjectDictionary:ghi:Integer", "3"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithDictionary(); config.Bind(options); Assert.Equal(3, options.ObjectDictionary.Count); Assert.Equal(1, options.ObjectDictionary["abc"].Integer); Assert.Equal(2, options.ObjectDictionary["def"].Integer); Assert.Equal(3, options.ObjectDictionary["ghi"].Integer); } [Fact] public void ListDictionary() { var input = new Dictionary { {"ListDictionary:abc:0", "abc_0"}, {"ListDictionary:abc:1", "abc_1"}, {"ListDictionary:def:0", "def_0"}, {"ListDictionary:def:1", "def_1"}, {"ListDictionary:def:2", "def_2"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithDictionary(); config.Bind(options); Assert.Equal(2, options.ListDictionary.Count); Assert.Equal(2, options.ListDictionary["abc"].Count); Assert.Equal(3, options.ListDictionary["def"].Count); Assert.Equal("abc_0", options.ListDictionary["abc"][0]); Assert.Equal("abc_1", options.ListDictionary["abc"][1]); Assert.Equal("def_0", options.ListDictionary["def"][0]); Assert.Equal("def_1", options.ListDictionary["def"][1]); Assert.Equal("def_2", options.ListDictionary["def"][2]); } [Fact] public void ListInNestedOptionBinding() { var input = new Dictionary { {"ObjectList:0:ListInNestedOption:0", "00"}, {"ObjectList:0:ListInNestedOption:1", "01"}, {"ObjectList:1:ListInNestedOption:0", "10"}, {"ObjectList:1:ListInNestedOption:1", "11"}, {"ObjectList:1:ListInNestedOption:2", "12"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithLists(); config.Bind(options); Assert.Equal(2, options.ObjectList.Count); Assert.Equal(2, options.ObjectList[0].ListInNestedOption.Count); Assert.Equal(3, options.ObjectList[1].ListInNestedOption.Count); Assert.Equal("00", options.ObjectList[0].ListInNestedOption[0]); Assert.Equal("01", options.ObjectList[0].ListInNestedOption[1]); Assert.Equal("10", options.ObjectList[1].ListInNestedOption[0]); Assert.Equal("11", options.ObjectList[1].ListInNestedOption[1]); Assert.Equal("12", options.ObjectList[1].ListInNestedOption[2]); } [Fact] public void NonStringKeyDictionaryBinding() { var input = new Dictionary { {"NonStringKeyDictionary:abc", "val_1"}, {"NonStringKeyDictionary:def", "val_2"}, {"NonStringKeyDictionary:ghi", "val_3"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithDictionary(); config.Bind(options); Assert.Empty(options.NonStringKeyDictionary); } [Fact] public void GetStringArray() { var input = new Dictionary { {"StringArray:0", "val0"}, {"StringArray:1", "val1"}, {"StringArray:2", "val2"}, {"StringArray:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithArrays(); config.Bind(options); var array = options.StringArray; Assert.Equal(4, array.Length); Assert.Equal("val0", array[0]); Assert.Equal("val1", array[1]); Assert.Equal("val2", array[2]); Assert.Equal("valx", array[3]); } [Fact] public void BindStringArray() { var input = new Dictionary { {"StringArray:0", "val0"}, {"StringArray:1", "val1"}, {"StringArray:2", "val2"}, {"StringArray:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var instance = new OptionsWithArrays(); config.Bind(instance); var array = instance.StringArray; Assert.Equal(4, array.Length); Assert.Equal("val0", array[0]); Assert.Equal("val1", array[1]); Assert.Equal("val2", array[2]); Assert.Equal("valx", array[3]); } [Fact] public void GetAlreadyInitializedArray() { var input = new Dictionary { {"AlreadyInitializedArray:0", "val0"}, {"AlreadyInitializedArray:1", "val1"}, {"AlreadyInitializedArray:2", "val2"}, {"AlreadyInitializedArray:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithArrays(); config.Bind(options); var array = options.AlreadyInitializedArray; Assert.Equal(7, array.Length); Assert.Equal(OptionsWithArrays.InitialValue, array[0]); Assert.Null(array[1]); Assert.Null(array[2]); Assert.Equal("val0", array[3]); Assert.Equal("val1", array[4]); Assert.Equal("val2", array[5]); Assert.Equal("valx", array[6]); } [Fact] public void BindAlreadyInitializedArray() { var input = new Dictionary { {"AlreadyInitializedArray:0", "val0"}, {"AlreadyInitializedArray:1", "val1"}, {"AlreadyInitializedArray:2", "val2"}, {"AlreadyInitializedArray:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithArrays(); config.Bind(options); var array = options.AlreadyInitializedArray; Assert.Equal(7, array.Length); Assert.Equal(OptionsWithArrays.InitialValue, array[0]); Assert.Null(array[1]); Assert.Null(array[2]); Assert.Equal("val0", array[3]); Assert.Equal("val1", array[4]); Assert.Equal("val2", array[5]); Assert.Equal("valx", array[6]); } [Fact] public void ArrayInNestedOptionBinding() { var input = new Dictionary { {"ObjectArray:0:ArrayInNestedOption:0", "0"}, {"ObjectArray:0:ArrayInNestedOption:1", "1"}, {"ObjectArray:1:ArrayInNestedOption:0", "10"}, {"ObjectArray:1:ArrayInNestedOption:1", "11"}, {"ObjectArray:1:ArrayInNestedOption:2", "12"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithArrays(); config.Bind(options); Assert.Equal(2, options.ObjectArray.Length); Assert.Equal(2, options.ObjectArray[0].ArrayInNestedOption.Length); Assert.Equal(3, options.ObjectArray[1].ArrayInNestedOption.Length); Assert.Equal(0, options.ObjectArray[0].ArrayInNestedOption[0]); Assert.Equal(1, options.ObjectArray[0].ArrayInNestedOption[1]); Assert.Equal(10, options.ObjectArray[1].ArrayInNestedOption[0]); Assert.Equal(11, options.ObjectArray[1].ArrayInNestedOption[1]); Assert.Equal(12, options.ObjectArray[1].ArrayInNestedOption[2]); } [Fact] public void UnsupportedMultidimensionalArrays() { var input = new Dictionary { {"DimensionalArray:0:0", "a"}, {"DimensionalArray:0:1", "b"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithArrays(); var exception = Assert.Throws( () => config.Bind(options)); Assert.Equal( Resources.FormatError_UnsupportedMultidimensionalArray(typeof(string[,])), exception.Message); } [Fact] public void JaggedArrayBinding() { var input = new Dictionary { {"JaggedArray:0:0", "00"}, {"JaggedArray:0:1", "01"}, {"JaggedArray:1:0", "10"}, {"JaggedArray:1:1", "11"}, {"JaggedArray:1:2", "12"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new OptionsWithArrays(); config.Bind(options); Assert.Equal(2, options.JaggedArray.Length); Assert.Equal(2, options.JaggedArray[0].Length); Assert.Equal(3, options.JaggedArray[1].Length); Assert.Equal("00", options.JaggedArray[0][0]); Assert.Equal("01", options.JaggedArray[0][1]); Assert.Equal("10", options.JaggedArray[1][0]); Assert.Equal("11", options.JaggedArray[1][1]); Assert.Equal("12", options.JaggedArray[1][2]); } [Fact] public void CanBindUninitializedIEnumerable() { var input = new Dictionary { {"IEnumerable:0", "val0"}, {"IEnumerable:1", "val1"}, {"IEnumerable:2", "val2"}, {"IEnumerable:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new UnintializedCollectionsOptions(); config.Bind(options); var array = options.IEnumerable.ToArray(); Assert.Equal(4, array.Length); Assert.Equal("val0", array[0]); Assert.Equal("val1", array[1]); Assert.Equal("val2", array[2]); Assert.Equal("valx", array[3]); } [Fact] public void CanBindUninitializedICollection() { var input = new Dictionary { {"ICollection:0", "val0"}, {"ICollection:1", "val1"}, {"ICollection:2", "val2"}, {"ICollection:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new UnintializedCollectionsOptions(); config.Bind(options); var array = options.ICollection.ToArray(); Assert.Equal(4, array.Length); Assert.Equal("val0", array[0]); Assert.Equal("val1", array[1]); Assert.Equal("val2", array[2]); Assert.Equal("valx", array[3]); } [Fact] public void CanBindUninitializedIReadOnlyCollection() { var input = new Dictionary { {"IReadOnlyCollection:0", "val0"}, {"IReadOnlyCollection:1", "val1"}, {"IReadOnlyCollection:2", "val2"}, {"IReadOnlyCollection:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new UnintializedCollectionsOptions(); config.Bind(options); var array = options.IReadOnlyCollection.ToArray(); Assert.Equal(4, array.Length); Assert.Equal("val0", array[0]); Assert.Equal("val1", array[1]); Assert.Equal("val2", array[2]); Assert.Equal("valx", array[3]); } [Fact] public void CanBindUninitializedIReadOnlyList() { var input = new Dictionary { {"IReadOnlyList:0", "val0"}, {"IReadOnlyList:1", "val1"}, {"IReadOnlyList:2", "val2"}, {"IReadOnlyList:x", "valx"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new UnintializedCollectionsOptions(); config.Bind(options); var array = options.IReadOnlyList.ToArray(); Assert.Equal(4, array.Length); Assert.Equal("val0", array[0]); Assert.Equal("val1", array[1]); Assert.Equal("val2", array[2]); Assert.Equal("valx", array[3]); } [Fact] public void CanBindUninitializedIDictionary() { var input = new Dictionary { {"IDictionary:abc", "val_1"}, {"IDictionary:def", "val_2"}, {"IDictionary:ghi", "val_3"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new UnintializedCollectionsOptions(); config.Bind(options); Assert.Equal(3, options.IDictionary.Count); Assert.Equal("val_1", options.IDictionary["abc"]); Assert.Equal("val_2", options.IDictionary["def"]); Assert.Equal("val_3", options.IDictionary["ghi"]); } [Fact] public void CanBindUninitializedIReadOnlyDictionary() { var input = new Dictionary { {"IReadOnlyDictionary:abc", "val_1"}, {"IReadOnlyDictionary:def", "val_2"}, {"IReadOnlyDictionary:ghi", "val_3"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(input); var config = configurationBuilder.Build(); var options = new UnintializedCollectionsOptions(); config.Bind(options); Assert.Equal(3, options.IReadOnlyDictionary.Count); Assert.Equal("val_1", options.IReadOnlyDictionary["abc"]); Assert.Equal("val_2", options.IReadOnlyDictionary["def"]); Assert.Equal("val_3", options.IReadOnlyDictionary["ghi"]); } private class UnintializedCollectionsOptions { public IEnumerable IEnumerable { get; set; } public IDictionary IDictionary { get; set; } public ICollection ICollection { get; set; } public IList IList { get; set; } public IReadOnlyCollection IReadOnlyCollection { get; set; } public IReadOnlyList IReadOnlyList { get; set; } public IReadOnlyDictionary IReadOnlyDictionary { get; set; } } private class CustomList : List { // Add an overload, just to make sure binding picks the right Add method public void Add(string a, string b) { } } private class CustomDictionary : Dictionary { } private class NestedOptions { public int Integer { get; set; } public List ListInNestedOption { get; set; } public int[] ArrayInNestedOption { get; set; } } private enum KeyEnum { abc, def, ghi } private class OptionsWithArrays { public const string InitialValue = "This was here before"; public OptionsWithArrays() { AlreadyInitializedArray = new string[] { InitialValue, null, null }; } public string[] AlreadyInitializedArray { get; set; } public string[] StringArray { get; set; } // this should throw becase we do not support multidimensional arrays public string[,] DimensionalArray { get; set; } public string[][] JaggedArray { get; set; } public NestedOptions[] ObjectArray { get; set; } } private class OptionsWithLists { public OptionsWithLists() { AlreadyInitializedList = new List { "This was here before" }; AlreadyInitializedListInterface = new List { "This was here too" }; } public CustomList CustomList { get; set; } public List StringList { get; set; } public List IntList { get; set; } // This cannot be initialized because we cannot // activate an interface public IList StringListInterface { get; set; } public List> NestedLists { get; set; } public List AlreadyInitializedList { get; set; } public List ObjectList { get; set; } public IList AlreadyInitializedListInterface { get; set; } } private class OptionsWithDictionary { public OptionsWithDictionary() { AlreadyInitializedStringDictionaryInterface = new Dictionary { ["123"] = "This was already here" }; } public Dictionary IntDictionary { get; set; } public Dictionary StringDictionary { get; set; } public Dictionary ObjectDictionary { get; set; } public Dictionary> ListDictionary { get; set; } public Dictionary NonStringKeyDictionary { get; set; } // This cannot be initialized because we cannot // activate an interface public IDictionary StringDictionaryInterface { get; set; } public IDictionary AlreadyInitializedStringDictionaryInterface { get; set; } } } } ================================================ FILE: test/Config.CommandLine.Test/CommandLineTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Configuration.Test; using Xunit; namespace Microsoft.Extensions.Configuration.CommandLine.Test { public class CommandLineTest { [Fact] public void IgnoresOnlyUnknownArgs() { var args = new string[] { "foo", "/bar=baz" }; var cmdLineConfig = new CommandLineConfigurationProvider(args); cmdLineConfig.Load(); Assert.Single(cmdLineConfig.GetChildKeys(new string[0], null)); Assert.Equal("baz", cmdLineConfig.Get("bar")); } [Fact] public void CanIgnoreValuesInMiddle() { var args = new string[] { "Key1=Value1", "--Key2=Value2", "/Key3=Value3", "Bogus1", "--Key4", "Value4", "Bogus2", "/Key5", "Value5", "Bogus3" }; var cmdLineConfig = new CommandLineConfigurationProvider(args); cmdLineConfig.Load(); Assert.Equal("Value1", cmdLineConfig.Get("Key1")); Assert.Equal("Value2", cmdLineConfig.Get("Key2")); Assert.Equal("Value3", cmdLineConfig.Get("Key3")); Assert.Equal("Value4", cmdLineConfig.Get("Key4")); Assert.Equal("Value5", cmdLineConfig.Get("Key5")); Assert.Equal(5, cmdLineConfig.GetChildKeys(new string[0], null).Count()); } [Fact] public void LoadKeyValuePairsFromCommandLineArgumentsWithoutSwitchMappings() { var args = new string[] { "Key1=Value1", "--Key2=Value2", "/Key3=Value3", "--Key4", "Value4", "/Key5", "Value5" }; var cmdLineConfig = new CommandLineConfigurationProvider(args); cmdLineConfig.Load(); Assert.Equal("Value1", cmdLineConfig.Get("Key1")); Assert.Equal("Value2", cmdLineConfig.Get("Key2")); Assert.Equal("Value3", cmdLineConfig.Get("Key3")); Assert.Equal("Value4", cmdLineConfig.Get("Key4")); Assert.Equal("Value5", cmdLineConfig.Get("Key5")); } [Fact] public void LoadKeyValuePairsFromCommandLineArgumentsWithSwitchMappings() { var args = new string[] { "-K1=Value1", "--Key2=Value2", "/Key3=Value3", "--Key4", "Value4", "/Key5", "Value5", "/Key6=Value6" }; var switchMappings = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "-K1", "LongKey1" }, { "--Key2", "SuperLongKey2" }, { "--Key6", "SuchALongKey6"} }; var cmdLineConfig = new CommandLineConfigurationProvider(args, switchMappings); cmdLineConfig.Load(); Assert.Equal("Value1", cmdLineConfig.Get("LongKey1")); Assert.Equal("Value2", cmdLineConfig.Get("SuperLongKey2")); Assert.Equal("Value3", cmdLineConfig.Get("Key3")); Assert.Equal("Value4", cmdLineConfig.Get("Key4")); Assert.Equal("Value5", cmdLineConfig.Get("Key5")); Assert.Equal("Value6", cmdLineConfig.Get("SuchALongKey6")); } [Fact] public void ThrowExceptionWhenPassingSwitchMappingsWithDuplicatedKeys() { // Arrange var args = new string[] { "-K1=Value1", "--Key2=Value2", "/Key3=Value3", "--Key4", "Value4", "/Key5", "Value5" }; var switchMappings = new Dictionary(StringComparer.Ordinal) { { "--KEY1", "LongKey1" }, { "--key1", "SuperLongKey1" }, { "-Key2", "LongKey2" }, { "-KEY2", "LongKey2"} }; // Find out the duplicate expected be be reported var expectedDup = string.Empty; var set = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var mapping in switchMappings) { if (set.Contains(mapping.Key)) { expectedDup = mapping.Key; break; } set.Add(mapping.Key); } var expectedMsg = new ArgumentException(Resources. FormatError_DuplicatedKeyInSwitchMappings(expectedDup), "switchMappings").Message; // Act var exception = Assert.Throws( () => new CommandLineConfigurationProvider(args, switchMappings)); // Assert Assert.Equal(expectedMsg, exception.Message); } [Fact] public void ThrowExceptionWhenSwitchMappingsContainInvalidKey() { var args = new string[] { "-K1=Value1", "--Key2=Value2", "/Key3=Value3", "--Key4", "Value4", "/Key5", "Value5" }; var switchMappings = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "-K1", "LongKey1" }, { "--Key2", "SuperLongKey2" }, { "/Key3", "AnotherSuperLongKey3" } }; var expectedMsg = new ArgumentException(Resources.FormatError_InvalidSwitchMapping("/Key3"), "switchMappings").Message; var exception = Assert.Throws( () => new CommandLineConfigurationProvider(args, switchMappings)); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void ThrowExceptionWhenNullIsPassedToConstructorAsArgs() { string[] args = null; var expectedMsg = new ArgumentNullException("args").Message; var exception = Assert.Throws(() => new CommandLineConfigurationProvider(args)); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void OverrideValueWhenKeyIsDuplicated() { var args = new string[] { "/Key1=Value1", "--Key1=Value2" }; var cmdLineConfig = new CommandLineConfigurationProvider(args); cmdLineConfig.Load(); Assert.Equal("Value2", cmdLineConfig.Get("Key1")); } [Fact] public void IgnoreWhenValueForAKeyIsMissing() { var args = new string[] { "--Key1", "Value1", "/Key2" /* The value for Key2 is missing here */ }; var cmdLineConfig = new CommandLineConfigurationProvider(args); cmdLineConfig.Load(); Assert.Single(cmdLineConfig.GetChildKeys(new string[0], null)); Assert.Equal("Value1", cmdLineConfig.Get("Key1")); } [Fact] public void IgnoreWhenAnArgumentCannotBeRecognized() { var args = new string[] { "ArgWithoutPrefixAndEqualSign" }; var cmdLineConfig = new CommandLineConfigurationProvider(args); cmdLineConfig.Load(); Assert.Empty(cmdLineConfig.GetChildKeys(new string[0], null)); } [Fact] public void IgnoreWhenShortSwitchNotDefined() { var args = new string[] { "-Key1", "Value1", }; var switchMappings = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "-Key2", "LongKey2" } }; var cmdLineConfig = new CommandLineConfigurationProvider(args, switchMappings); cmdLineConfig.Load(); Assert.Empty(cmdLineConfig.GetChildKeys(new string[0], "")); } } } ================================================ FILE: test/Config.CommandLine.Test/Config.CommandLine.Test.csproj ================================================  Microsoft.Extensions.Configuration.CommandLine.Test Microsoft.Extensions.Configuration.CommandLine.Test $(StandardTestTfms) ================================================ FILE: test/Config.EnvironmentVariables.Test/Config.EnvironmentVariables.Test.csproj ================================================  Microsoft.Extensions.Configuration.EnvironmentVariables.Test Microsoft.Extensions.Configuration.EnvironmentVariables.Test $(StandardTestTfms) ================================================ FILE: test/Config.EnvironmentVariables.Test/EnvironmentVariablesTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using Microsoft.Extensions.Configuration.Test; using Xunit; namespace Microsoft.Extensions.Configuration.EnvironmentVariables.Test { public class EnvironmentVariablesTest { [Fact] public void LoadKeyValuePairsFromEnvironmentDictionary() { var dict = new Hashtable() { {"DefaultConnection:ConnectionString", "TestConnectionString"}, {"DefaultConnection:Provider", "SqlClient"}, {"Inventory:ConnectionString", "AnotherTestConnectionString"}, {"Inventory:Provider", "MySql"} }; var envConfigSrc = new EnvironmentVariablesConfigurationProvider(null); envConfigSrc.Load(dict); Assert.Equal("TestConnectionString", envConfigSrc.Get("defaultconnection:ConnectionString")); Assert.Equal("SqlClient", envConfigSrc.Get("DEFAULTCONNECTION:PROVIDER")); Assert.Equal("AnotherTestConnectionString", envConfigSrc.Get("Inventory:CONNECTIONSTRING")); Assert.Equal("MySql", envConfigSrc.Get("Inventory:Provider")); } [Fact] public void LoadKeyValuePairsFromEnvironmentDictionaryWithPrefix() { var dict = new Hashtable() { {"DefaultConnection:ConnectionString", "TestConnectionString"}, {"DefaultConnection:Provider", "SqlClient"}, {"Inventory:ConnectionString", "AnotherTestConnectionString"}, {"Inventory:Provider", "MySql"} }; var envConfigSrc = new EnvironmentVariablesConfigurationProvider("DefaultConnection:"); envConfigSrc.Load(dict); Assert.Equal("TestConnectionString", envConfigSrc.Get("ConnectionString")); Assert.Equal("SqlClient", envConfigSrc.Get("Provider")); } [Fact] public void LoadKeyValuePairsFromAzureEnvironment() { var dict = new Hashtable() { {"APPSETTING_AppName", "TestAppName"}, {"CUSTOMCONNSTR_db1", "CustomConnStr"}, {"SQLCONNSTR_db2", "SQLConnStr"}, {"MYSQLCONNSTR_db3", "MySQLConnStr"}, {"SQLAZURECONNSTR_db4", "SQLAzureConnStr"}, {"CommonEnv", "CommonEnvValue"}, }; var envConfigSrc = new EnvironmentVariablesConfigurationProvider(); envConfigSrc.Load(dict); string value; Assert.Equal("TestAppName", envConfigSrc.Get("APPSETTING_AppName")); Assert.False(envConfigSrc.TryGet("AppName", out value)); Assert.Equal("CustomConnStr", envConfigSrc.Get("ConnectionStrings:db1")); Assert.Equal("SQLConnStr", envConfigSrc.Get("ConnectionStrings:db2")); Assert.Equal("System.Data.SqlClient", envConfigSrc.Get("ConnectionStrings:db2_ProviderName")); Assert.Equal("MySQLConnStr", envConfigSrc.Get("ConnectionStrings:db3")); Assert.Equal("MySql.Data.MySqlClient", envConfigSrc.Get("ConnectionStrings:db3_ProviderName")); Assert.Equal("SQLAzureConnStr", envConfigSrc.Get("ConnectionStrings:db4")); Assert.Equal("System.Data.SqlClient", envConfigSrc.Get("ConnectionStrings:db4_ProviderName")); Assert.Equal("CommonEnvValue", envConfigSrc.Get("CommonEnv")); } [Fact] public void LoadKeyValuePairsFromAzureEnvironmentWithPrefix() { var dict = new Hashtable() { {"CUSTOMCONNSTR_db1", "CustomConnStr"}, {"SQLCONNSTR_db2", "SQLConnStr"}, {"MYSQLCONNSTR_db3", "MySQLConnStr"}, {"SQLAZURECONNSTR_db4", "SQLAzureConnStr"}, {"CommonEnv", "CommonEnvValue"}, }; var envConfigSrc = new EnvironmentVariablesConfigurationProvider("ConnectionStrings:"); envConfigSrc.Load(dict); Assert.Equal("CustomConnStr", envConfigSrc.Get("db1")); Assert.Equal("SQLConnStr", envConfigSrc.Get("db2")); Assert.Equal("System.Data.SqlClient", envConfigSrc.Get("db2_ProviderName")); Assert.Equal("MySQLConnStr", envConfigSrc.Get("db3")); Assert.Equal("MySql.Data.MySqlClient", envConfigSrc.Get("db3_ProviderName")); Assert.Equal("SQLAzureConnStr", envConfigSrc.Get("db4")); Assert.Equal("System.Data.SqlClient", envConfigSrc.Get("db4_ProviderName")); } [Fact] public void LastVariableAddedWhenKeyIsDuplicatedInAzureEnvironment() { var dict = new Hashtable() { {"ConnectionStrings:db2", "CommonEnvValue"}, {"SQLCONNSTR_db2", "SQLConnStr"}, }; var envConfigSrc = new EnvironmentVariablesConfigurationProvider(); envConfigSrc.Load(dict); Assert.True(!string.IsNullOrEmpty(envConfigSrc.Get("ConnectionStrings:db2"))); Assert.Equal("System.Data.SqlClient", envConfigSrc.Get("ConnectionStrings:db2_ProviderName")); } [Fact] public void LastVariableAddedWhenMultipleEnvironmentVariablesWithSameNameButDifferentCaseExist() { var dict = new Hashtable() { {"CommonEnv", "CommonEnvValue1"}, {"commonenv", "commonenvValue2"}, {"cOMMonEnv", "commonenvValue3"}, }; var envConfigSrc = new EnvironmentVariablesConfigurationProvider(); envConfigSrc.Load(dict); Assert.True(!string.IsNullOrEmpty(envConfigSrc.Get("cOMMonEnv"))); Assert.True(!string.IsNullOrEmpty(envConfigSrc.Get("CommonEnv"))); } [Fact] public void ReplaceDoubleUnderscoreInEnvironmentVariables() { var dict = new Hashtable() { {"data__ConnectionString", "connection"}, {"SQLCONNSTR__db1", "connStr"} }; var envConfigSrc = new EnvironmentVariablesConfigurationProvider(); envConfigSrc.Load(dict); Assert.Equal("connection", envConfigSrc.Get("data:ConnectionString")); Assert.Equal("System.Data.SqlClient", envConfigSrc.Get("ConnectionStrings:_db1_ProviderName")); } } } ================================================ FILE: test/Config.FileExtensions.Test/Config.FileExtensions.Test.csproj ================================================  Microsoft.Extensions.Configuration.FileExtensions.Test Microsoft.Extensions.Configuration.FileExtensions.Test $(StandardTestTfms) ================================================ FILE: test/Config.FileExtensions.Test/FileConfigurationBuilderExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using Microsoft.Extensions.FileProviders; using Xunit; namespace Microsoft.Extensions.Configuration.Json { public class FileConfigurationBuilderExtensionsTest { [Fact] public void SetFileProvider_ThrowsIfBasePathIsNull() { // Arrange var configurationBuilder = new ConfigurationBuilder(); // Act and Assert var ex = Assert.Throws(() => configurationBuilder.SetBasePath(basePath: null)); Assert.Equal("basePath", ex.ParamName); } [Fact] public void SetFileProvider_CheckPropertiesValueOnBuilder() { var expectedBasePath = Directory.GetCurrentDirectory(); var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.SetBasePath(expectedBasePath); var physicalProvider = configurationBuilder.GetFileProvider() as PhysicalFileProvider; Assert.NotNull(physicalProvider); Assert.Equal(EnsureTrailingSlash(expectedBasePath), physicalProvider.Root); } [Fact] public void GetFileProvider_ReturnPhysicalProviderWithBaseDirectoryIfNotSet() { // Arrange var configurationBuilder = new ConfigurationBuilder(); // Act var physicalProvider = configurationBuilder.GetFileProvider() as PhysicalFileProvider; string expectedPath; expectedPath = AppContext.BaseDirectory; Assert.NotNull(physicalProvider); Assert.Equal(EnsureTrailingSlash(expectedPath), physicalProvider.Root); } private static string EnsureTrailingSlash(string path) { if (!string.IsNullOrEmpty(path) && path[path.Length - 1] != Path.DirectorySeparatorChar) { return path + Path.DirectorySeparatorChar; } return path; } } } ================================================ FILE: test/Config.FunctionalTests/ArrayTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using System.Linq; using Xunit; namespace Microsoft.Extensions.Configuration.FunctionalTests { public class ArrayTests : IDisposable { private string _iniConfigFilePath; private string _xmlConfigFilePath; private string _json1ConfigFilePath; private string _json2ConfigFilePath; private static readonly string _iniConfigFileContent = @" [address] 2=ini_2.2.2.2 i=ini_i.i.i.i "; private static readonly string _xmlConfigFileContent = @"
xml_4.4.4.4
xml_1.1.1.1
xml_x.x.x.x
"; private static readonly string _json1ConfigFileContent = @" { 'address': [ 'json_0.0.0.0', 'json_1.1.1.1', 'json_2.2.2.2' ] } "; private static readonly string _json2ConfigFileContent = @" { 'address': { 'j': 'json_j.j.j.j', '3': 'json_3.3.3.3' } } "; [Fact] public void DifferentConfigSources_Merged_KeysAreSorted() { var config = BuildConfig(); var configurationSection = config.GetSection("address"); var indexConfigurationSections = configurationSection.GetChildren().ToArray(); Assert.Equal(8, indexConfigurationSections.Length); Assert.Equal("0", indexConfigurationSections[0].Key); Assert.Equal("1", indexConfigurationSections[1].Key); Assert.Equal("2", indexConfigurationSections[2].Key); Assert.Equal("3", indexConfigurationSections[3].Key); Assert.Equal("4", indexConfigurationSections[4].Key); Assert.Equal("i", indexConfigurationSections[5].Key); Assert.Equal("j", indexConfigurationSections[6].Key); Assert.Equal("x", indexConfigurationSections[7].Key); Assert.Equal("address:0", indexConfigurationSections[0].Path); Assert.Equal("address:1", indexConfigurationSections[1].Path); Assert.Equal("address:2", indexConfigurationSections[2].Path); Assert.Equal("address:3", indexConfigurationSections[3].Path); Assert.Equal("address:4", indexConfigurationSections[4].Path); Assert.Equal("address:i", indexConfigurationSections[5].Path); Assert.Equal("address:j", indexConfigurationSections[6].Path); Assert.Equal("address:x", indexConfigurationSections[7].Path); } [Fact] public void DifferentConfigSources_Merged_WithOverwrites() { var config = BuildConfig(); Assert.Equal("json_0.0.0.0", config["address:0"]); Assert.Equal("xml_1.1.1.1", config["address:1"]); Assert.Equal("ini_2.2.2.2", config["address:2"]); Assert.Equal("json_3.3.3.3", config["address:3"]); Assert.Equal("xml_4.4.4.4", config["address:4"]); Assert.Equal("ini_i.i.i.i", config["address:i"]); Assert.Equal("json_j.j.j.j", config["address:j"]); Assert.Equal("xml_x.x.x.x", config["address:x"]); } private IConfiguration BuildConfig() { var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddJsonFile(_json1ConfigFilePath); configurationBuilder.AddIniFile(_iniConfigFilePath); configurationBuilder.AddJsonFile(_json2ConfigFilePath); configurationBuilder.AddXmlFile(_xmlConfigFilePath); return configurationBuilder.Build(); } public ArrayTests() { var basePath = AppContext.BaseDirectory ?? string.Empty; _iniConfigFilePath = Path.GetRandomFileName(); _xmlConfigFilePath = Path.GetRandomFileName(); _json1ConfigFilePath = Path.GetRandomFileName(); _json2ConfigFilePath = Path.GetRandomFileName(); File.WriteAllText(Path.Combine(basePath, _iniConfigFilePath), _iniConfigFileContent); File.WriteAllText(Path.Combine(basePath, _xmlConfigFilePath), _xmlConfigFileContent); File.WriteAllText(Path.Combine(basePath, _json1ConfigFilePath), _json1ConfigFileContent); File.WriteAllText(Path.Combine(basePath, _json2ConfigFilePath), _json2ConfigFileContent); } public void Dispose() { File.Delete(_iniConfigFilePath); File.Delete(_xmlConfigFilePath); File.Delete(_json1ConfigFilePath); File.Delete(_json2ConfigFilePath); } } } ================================================ FILE: test/Config.FunctionalTests/Config.FunctionalTests.csproj ================================================  Microsoft.Extensions.Configuration.FunctionalTests Microsoft.Extensions.Configuration.FunctionalTests $(StandardTestTfms) ================================================ FILE: test/Config.FunctionalTests/ConfigurationTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Configuration.Ini; using Microsoft.Extensions.Configuration.Json; using Microsoft.Extensions.Configuration.Xml; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; using Xunit; namespace Microsoft.Extensions.Configuration.Test { public class ConfigurationTests : IDisposable { private readonly DisposableFileSystem _fileSystem; private readonly PhysicalFileProvider _fileProvider; private readonly string _basePath; private readonly string _iniFile; private readonly string _xmlFile; private readonly string _jsonFile; private static readonly string _iniConfigFileContent = @"IniKey1=IniValue1 [IniKey2] # Comments IniKey3=IniValue2 ; Comments IniKey4=IniValue3 IniKey5:IniKey6=IniValue4 /Comments [CommonKey1:CommonKey2] IniKey7=IniValue5 CommonKey3:CommonKey4=IniValue6"; private static readonly string _xmlConfigFileContent = @" XmlValue3 XmlValue4 "; private static readonly string _jsonConfigFileContent = @"{ ""JsonKey1"": ""JsonValue1"", ""Json.Key2"": { ""JsonKey3"": ""JsonValue2"", ""Json.Key4"": ""JsonValue3"", ""JsonKey5:JsonKey6"": ""JsonValue4"" }, ""CommonKey1"": { ""CommonKey2"": { ""JsonKey7"": ""JsonValue5"", ""CommonKey3:CommonKey4"": ""JsonValue6"" } } }"; private static readonly Dictionary _memConfigContent = new Dictionary { { "MemKey1", "MemValue1" }, { "MemKey2:MemKey3", "MemValue2" }, { "MemKey2:MemKey4", "MemValue3" }, { "MemKey2:MemKey5:MemKey6", "MemValue4" }, { "CommonKey1:CommonKey2:MemKey7", "MemValue5" }, { "CommonKey1:CommonKey2:CommonKey3:CommonKey4", "MemValue6" } }; public ConfigurationTests() { _fileSystem = new DisposableFileSystem(); _fileProvider = new PhysicalFileProvider(_fileSystem.RootPath); _basePath = AppContext.BaseDirectory ?? string.Empty; _iniFile = Path.GetRandomFileName(); _xmlFile = Path.GetRandomFileName(); _jsonFile = Path.GetRandomFileName(); } [Fact] public void MissingFileIncludesAbsolutePathIfPhysicalFileProvider() { var error = Assert.Throws(() => new ConfigurationBuilder().AddIniFile("missing.ini").Build()); Assert.True(error.Message.Contains(_basePath), error.Message); error = Assert.Throws(() => new ConfigurationBuilder().AddJsonFile("missing.json").Build()); Assert.True(error.Message.Contains(_basePath), error.Message); error = Assert.Throws(() => new ConfigurationBuilder().AddXmlFile("missing.xml").Build()); Assert.True(error.Message.Contains(_basePath), error.Message); } private class NotVeryGoodFileProvider : IFileProvider { public IDirectoryContents GetDirectoryContents(string subpath) { return null; } public IFileInfo GetFileInfo(string subpath) { return null; } public IChangeToken Watch(string filter) { return null; } } private class MissingFile : IFileInfo { public bool Exists { get { return false; } } public bool IsDirectory { get { throw new NotImplementedException(); } } public DateTimeOffset LastModified { get { throw new NotImplementedException(); } } public long Length { get { throw new NotImplementedException(); } } public string Name { get { throw new NotImplementedException(); } } public string PhysicalPath { get { return null; } } public Stream CreateReadStream() { throw new NotImplementedException(); } } private class AlwaysMissingFileProvider : IFileProvider { public IDirectoryContents GetDirectoryContents(string subpath) { return null; } public IFileInfo GetFileInfo(string subpath) { return new MissingFile(); } public IChangeToken Watch(string filter) { return null; } } private void WriteTestFiles() { _fileSystem.WriteFile(_iniFile, _iniConfigFileContent); _fileSystem.WriteFile(_xmlFile, _xmlConfigFileContent); _fileSystem.WriteFile(_jsonFile, _jsonConfigFileContent); } private IConfigurationBuilder CreateBuilder() { return new ConfigurationBuilder().SetFileProvider(_fileProvider); } [Fact] public void MissingFileDoesNotIncludesAbsolutePathIfWithNullFileInfo() { var provider = new NotVeryGoodFileProvider(); var error = Assert.Throws(() => new ConfigurationBuilder().AddIniFile(provider, "missing.ini", optional: false, reloadOnChange: false).Build()); Assert.False(error.Message.Contains(_basePath), error.Message); error = Assert.Throws(() => new ConfigurationBuilder().AddJsonFile(provider, "missing.json", optional: false, reloadOnChange: false).Build()); Assert.False(error.Message.Contains(_basePath), error.Message); error = Assert.Throws(() => new ConfigurationBuilder().AddXmlFile(provider, "missing.xml", optional: false, reloadOnChange: false).Build()); Assert.False(error.Message.Contains(_basePath), error.Message); } [Fact] public void MissingFileDoesNotIncludesAbsolutePathIfWithNoPhysicalPath() { var provider = new AlwaysMissingFileProvider(); var error = Assert.Throws(() => new ConfigurationBuilder().AddIniFile(provider, "missing.ini", optional: false, reloadOnChange: false).Build()); Assert.False(error.Message.Contains(_basePath), error.Message); error = Assert.Throws(() => new ConfigurationBuilder().AddJsonFile(provider, "missing.json", optional: false, reloadOnChange: false).Build()); Assert.False(error.Message.Contains(_basePath), error.Message); error = Assert.Throws(() => new ConfigurationBuilder().AddXmlFile(provider, "missing.xml", optional: false, reloadOnChange: false).Build()); Assert.False(error.Message.Contains(_basePath), error.Message); } [Fact] public void LoadAndCombineKeyValuePairsFromDifferentConfigurationProviders() { // Arrange _fileSystem.WriteFile(_iniFile, _iniConfigFileContent); _fileSystem.WriteFile(_xmlFile, _xmlConfigFileContent); _fileSystem.WriteFile(_jsonFile, _jsonConfigFileContent); var config = CreateBuilder() .AddIniFile(_iniFile) .AddJsonFile(_jsonFile) .AddXmlFile(_xmlFile) .AddInMemoryCollection(_memConfigContent) .Build(); // Assert Assert.Equal("IniValue1", config["IniKey1"]); Assert.Equal("IniValue2", config["IniKey2:IniKey3"]); Assert.Equal("IniValue3", config["IniKey2:IniKey4"]); Assert.Equal("IniValue4", config["IniKey2:IniKey5:IniKey6"]); Assert.Equal("IniValue5", config["CommonKey1:CommonKey2:IniKey7"]); Assert.Equal("JsonValue1", config["JsonKey1"]); Assert.Equal("JsonValue2", config["Json.Key2:JsonKey3"]); Assert.Equal("JsonValue3", config["Json.Key2:Json.Key4"]); Assert.Equal("JsonValue4", config["Json.Key2:JsonKey5:JsonKey6"]); Assert.Equal("JsonValue5", config["CommonKey1:CommonKey2:JsonKey7"]); Assert.Equal("XmlValue1", config["XmlKey1"]); Assert.Equal("XmlValue2", config["XmlKey2:XmlKey3"]); Assert.Equal("XmlValue3", config["XmlKey2:XmlKey4"]); Assert.Equal("XmlValue4", config["XmlKey2:XmlKey5:XmlKey6"]); Assert.Equal("XmlValue5", config["CommonKey1:CommonKey2:XmlKey7"]); Assert.Equal("MemValue1", config["MemKey1"]); Assert.Equal("MemValue2", config["MemKey2:MemKey3"]); Assert.Equal("MemValue3", config["MemKey2:MemKey4"]); Assert.Equal("MemValue4", config["MemKey2:MemKey5:MemKey6"]); Assert.Equal("MemValue5", config["CommonKey1:CommonKey2:MemKey7"]); Assert.Equal("MemValue6", config["CommonKey1:CommonKey2:CommonKey3:CommonKey4"]); } [Fact] public void LoadAndCombineKeyValuePairsFromDifferentConfigurationProvidersWithAbsolutePath() { // Arrange WriteTestFiles(); var configurationBuilder = new ConfigurationBuilder(); // Act configurationBuilder.AddIniFile(Path.Combine(_fileSystem.RootPath, _iniFile)); configurationBuilder.AddJsonFile(Path.Combine(_fileSystem.RootPath, _jsonFile)); configurationBuilder.AddXmlFile(Path.Combine(_fileSystem.RootPath, _xmlFile)); configurationBuilder.AddInMemoryCollection(_memConfigContent); var config = configurationBuilder.Build(); // Assert Assert.Equal("IniValue1", config["IniKey1"]); Assert.Equal("IniValue2", config["IniKey2:IniKey3"]); Assert.Equal("IniValue3", config["IniKey2:IniKey4"]); Assert.Equal("IniValue4", config["IniKey2:IniKey5:IniKey6"]); Assert.Equal("IniValue5", config["CommonKey1:CommonKey2:IniKey7"]); Assert.Equal("JsonValue1", config["JsonKey1"]); Assert.Equal("JsonValue2", config["Json.Key2:JsonKey3"]); Assert.Equal("JsonValue3", config["Json.Key2:Json.Key4"]); Assert.Equal("JsonValue4", config["Json.Key2:JsonKey5:JsonKey6"]); Assert.Equal("JsonValue5", config["CommonKey1:CommonKey2:JsonKey7"]); Assert.Equal("XmlValue1", config["XmlKey1"]); Assert.Equal("XmlValue2", config["XmlKey2:XmlKey3"]); Assert.Equal("XmlValue3", config["XmlKey2:XmlKey4"]); Assert.Equal("XmlValue4", config["XmlKey2:XmlKey5:XmlKey6"]); Assert.Equal("XmlValue5", config["CommonKey1:CommonKey2:XmlKey7"]); Assert.Equal("MemValue1", config["MemKey1"]); Assert.Equal("MemValue2", config["MemKey2:MemKey3"]); Assert.Equal("MemValue3", config["MemKey2:MemKey4"]); Assert.Equal("MemValue4", config["MemKey2:MemKey5:MemKey6"]); Assert.Equal("MemValue5", config["CommonKey1:CommonKey2:MemKey7"]); Assert.Equal("MemValue6", config["CommonKey1:CommonKey2:CommonKey3:CommonKey4"]); } [Fact] public void CanOverrideValuesWithNewConfigurationProvider() { // Arrange WriteTestFiles(); var configurationBuilder = CreateBuilder(); // Act & Assert configurationBuilder.AddIniFile(_iniFile); var config = configurationBuilder.Build(); Assert.Equal("IniValue6", config["CommonKey1:CommonKey2:CommonKey3:CommonKey4"]); configurationBuilder.AddJsonFile(_jsonFile); config = configurationBuilder.Build(); Assert.Equal("JsonValue6", config["CommonKey1:CommonKey2:CommonKey3:CommonKey4"]); configurationBuilder.AddXmlFile(_xmlFile); config = configurationBuilder.Build(); Assert.Equal("XmlValue6", config["CommonKey1:CommonKey2:CommonKey3:CommonKey4"]); configurationBuilder.AddInMemoryCollection(_memConfigContent); config = configurationBuilder.Build(); Assert.Equal("MemValue6", config["CommonKey1:CommonKey2:CommonKey3:CommonKey4"]); } private IConfigurationRoot BuildConfig() { var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddIniFile(Path.GetFileName(_iniFile)); configurationBuilder.AddJsonFile(Path.GetFileName(_jsonFile)); configurationBuilder.AddXmlFile(Path.GetFileName(_xmlFile)); return configurationBuilder.Build(); } public class TestIniSourceProvider : IniConfigurationProvider, IConfigurationSource { public TestIniSourceProvider(string path) : base(new IniConfigurationSource { Path = path }) { } public IConfigurationProvider Build(IConfigurationBuilder builder) { Source.FileProvider = builder.GetFileProvider(); return this; } } public class TestJsonSourceProvider : JsonConfigurationProvider, IConfigurationSource { public TestJsonSourceProvider(string path) : base(new JsonConfigurationSource { Path = path }) { } public IConfigurationProvider Build(IConfigurationBuilder builder) { Source.FileProvider = builder.GetFileProvider(); return this; } } public class TestXmlSourceProvider : XmlConfigurationProvider, IConfigurationSource { public TestXmlSourceProvider(string path) : base(new XmlConfigurationSource { Path = path }) { } public IConfigurationProvider Build(IConfigurationBuilder builder) { Source.FileProvider = builder.GetFileProvider(); return this; } } [Fact] public void OnLoadErrorWillBeCalledOnJsonParseError() { // Arrange File.WriteAllText(Path.Combine(_basePath, "error.json"), @"{""JsonKey1"": "); FileConfigurationProvider provider = null; Exception jsonError = null; Action jsonLoadError = c => { jsonError = c.Exception; provider = c.Provider; }; try { new ConfigurationBuilder().AddJsonFile("error.json") .SetFileLoadExceptionHandler(jsonLoadError) .Build(); } catch (FormatException e) { Assert.Equal(e, jsonError); } Assert.NotNull(provider); } [Fact] public void OnLoadErrorWillBeCalledOnXmlParseError() { // Arrange _fileSystem.WriteFile("error.xml", @"gobblygook"); FileConfigurationProvider provider = null; Exception error = null; Action loadError = c => { error = c.Exception; provider = c.Provider; }; try { CreateBuilder().AddJsonFile("error.xml") .SetFileLoadExceptionHandler(loadError) .Build(); } catch (FormatException e) { Assert.Equal(e, error); } Assert.NotNull(provider); } [Fact] public void OnLoadErrorWillBeCalledOnIniLoadError() { // Arrange _fileSystem.WriteFile("error.ini", @"IniKey1=IniValue1 IniKey1=IniValue2"); FileConfigurationProvider provider = null; Exception error = null; Action loadError = c => { error = c.Exception; provider = c.Provider; }; try { CreateBuilder().AddIniFile("error.ini") .SetFileLoadExceptionHandler(loadError) .Build(); } catch (FormatException e) { Assert.Equal(e, error); } Assert.NotNull(provider); } [Fact] public void OnLoadErrorCanIgnoreErrors() { // Arrange _fileSystem.WriteFile("error.json", @"{""JsonKey1"": "); FileConfigurationProvider provider = null; Action jsonLoadError = c => { provider = c.Provider; c.Ignore = true; }; CreateBuilder() .AddJsonFile(s => { s.Path = "error.json"; s.OnLoadException = jsonLoadError; }) .Build(); Assert.NotNull(provider); } [Fact] public void CanSetValuesAndReloadValues() { // Arrange WriteTestFiles(); var configurationBuilder = CreateBuilder(); configurationBuilder.Add(new TestIniSourceProvider(_iniFile)); configurationBuilder.Add(new TestJsonSourceProvider(_jsonFile)); configurationBuilder.Add(new TestXmlSourceProvider(_xmlFile)); var config = configurationBuilder.Build(); // Act & Assert // Set value config["CommonKey1:CommonKey2:CommonKey3:CommonKey4"] = "NewValue"; // All config sources must be updated foreach (var provider in configurationBuilder.Sources) { Assert.Equal("NewValue", (provider as FileConfigurationProvider).Get("CommonKey1:CommonKey2:CommonKey3:CommonKey4")); } // Recover values by reloading config.Reload(); Assert.Equal("XmlValue6", config["CommonKey1:CommonKey2:CommonKey3:CommonKey4"]); // Set value with indexer config["CommonKey1:CommonKey2:CommonKey3:CommonKey4"] = "NewValue"; // All config sources must be updated foreach (var provider in configurationBuilder.Sources) { Assert.Equal("NewValue", (provider as FileConfigurationProvider).Get("CommonKey1:CommonKey2:CommonKey3:CommonKey4")); } // Recover values by reloading config.Reload(); Assert.Equal("XmlValue6", config["CommonKey1:CommonKey2:CommonKey3:CommonKey4"]); } [Fact] public async Task ReloadOnChangeWorksAfterError() { _fileSystem.WriteFile("reload.json", @"{""JsonKey1"": ""JsonValue1""}"); var config = CreateBuilder() .AddJsonFile("reload.json", optional: false, reloadOnChange: true) .Build(); Assert.Equal("JsonValue1", config["JsonKey1"]); // Introduce an error and make sure the old key is removed _fileSystem.WriteFile("reload.json", @"{""JsonKey1"": "); var i = 0; while (i < 10 && config["JsonKey1"] != null) { await Task.Delay(2000); // wait for notification i++; } Assert.Null(config["JsonKey1"]); // Update the file again to make sure the config is updated _fileSystem.WriteFile("reload.json", @"{""JsonKey1"": ""JsonValue2""}"); i = 0; while (i < 10 && config["JsonKey1"] == null) { await Task.Delay(1100); // wait for notification i++; } Assert.Equal("JsonValue2", config["JsonKey1"]); } [Fact] public async Task TouchingFileWillReload() { // Arrange _fileSystem.WriteFile("reload.json", @"{""JsonKey1"": ""JsonValue1""}"); _fileSystem.WriteFile("reload.ini", @"IniKey1 = IniValue1"); _fileSystem.WriteFile("reload.xml", @""); var config = CreateBuilder() .AddIniFile("reload.ini", optional: false, reloadOnChange: true) .AddJsonFile("reload.json", optional: false, reloadOnChange: true) .AddXmlFile("reload.xml", optional: false, reloadOnChange: true) .Build(); Assert.Equal("JsonValue1", config["JsonKey1"]); Assert.Equal("IniValue1", config["IniKey1"]); Assert.Equal("XmlValue1", config["XmlKey1"]); var token = config.GetReloadToken(); // Act & Assert // Update files _fileSystem.WriteFile("reload.json", @"{""JsonKey1"": ""JsonValue2""}"); _fileSystem.WriteFile("reload.ini", @"IniKey1 = IniValue2"); _fileSystem.WriteFile("reload.xml", @""); await Task.Delay(2000); Assert.Equal("JsonValue2", config["JsonKey1"]); Assert.Equal("IniValue2", config["IniKey1"]); Assert.Equal("XmlValue2", config["XmlKey1"]); Assert.True(token.HasChanged); } [Fact] public async Task CreatingOptionalFileInNonExistentDirectoryWillReload() { var directory = Path.GetRandomFileName(); var jsonRootRelativeFile = Path.Combine(directory, Path.GetRandomFileName()); var jsonAbsoluteFile = Path.Combine(_fileSystem.RootPath, jsonRootRelativeFile); // Arrange var config = new ConfigurationBuilder() .AddJsonFile(jsonAbsoluteFile, optional: true, reloadOnChange: true) .Build(); Assert.Null(config["JsonKey1"]); var createToken = config.GetReloadToken(); Assert.False(createToken.HasChanged); _fileSystem.CreateFolder(directory); await Task.Delay(500); _fileSystem.WriteFile(jsonRootRelativeFile, @"{""JsonKey1"": ""JsonValue1""}"); await Task.Delay(2500); Assert.Equal("JsonValue1", config["JsonKey1"]); Assert.True(createToken.HasChanged); } [Theory] [InlineData(false)] [InlineData(true)] public async Task DeletingFilesThatRedefineKeysWithReload(bool optional) { // Arrange _fileSystem.WriteFile(_jsonFile, @"{""Key"": ""JsonValue1""}"); _fileSystem.WriteFile(_iniFile, @"Key = IniValue1"); _fileSystem.WriteFile(_xmlFile, @""); // Act & Assert var config = CreateBuilder() .AddXmlFile(_xmlFile, optional, reloadOnChange: true) .AddJsonFile(_jsonFile, optional, reloadOnChange: true) .AddIniFile(_iniFile, optional, reloadOnChange: true) .Build(); Assert.Equal("IniValue1", config["Key"]); // Delete files and ensure order is preserved var token = config.GetReloadToken(); _fileSystem.DeleteFile(_iniFile); await Task.Delay(2000); Assert.Equal("JsonValue1", config["Key"]); Assert.True(token.HasChanged); token = config.GetReloadToken(); _fileSystem.DeleteFile(_jsonFile); await Task.Delay(2000); Assert.Equal("XmlValue1", config["Key"]); Assert.True(token.HasChanged); token = config.GetReloadToken(); _fileSystem.DeleteFile(_xmlFile); await Task.Delay(2000); Assert.Null(config["Key"]); Assert.True(token.HasChanged); token = config.GetReloadToken(); _fileSystem.WriteFile(_jsonFile, @"{""Key"": ""JsonValue1""}"); await Task.Delay(2000); Assert.Equal("JsonValue1", config["Key"]); Assert.True(token.HasChanged); // Adding a file earlier in the chain has no effect token = config.GetReloadToken(); _fileSystem.WriteFile(_xmlFile, @""); await Task.Delay(2000); Assert.Equal("JsonValue1", config["Key"]); Assert.True(token.HasChanged); token = config.GetReloadToken(); _fileSystem.WriteFile(_iniFile, @"Key = IniValue1"); await Task.Delay(2000); Assert.Equal("IniValue1", config["Key"]); Assert.True(token.HasChanged); } [Theory] [InlineData(false)] [InlineData(true)] public async Task DeletingFileWillReload(bool optional) { // Arrange _fileSystem.WriteFile(_jsonFile, @"{""JsonKey1"": ""JsonValue1""}"); _fileSystem.WriteFile(_iniFile, @"IniKey1 = IniValue1"); _fileSystem.WriteFile(_xmlFile, @""); // Act & Assert var config = CreateBuilder() .AddXmlFile(_xmlFile, optional, reloadOnChange: true) .AddJsonFile(_jsonFile, optional, reloadOnChange: true) .AddIniFile(_iniFile, optional, reloadOnChange: true) .Build(); Assert.Equal("JsonValue1", config["JsonKey1"]); Assert.Equal("IniValue1", config["IniKey1"]); Assert.Equal("XmlValue1", config["XmlKey1"]); var token = config.GetReloadToken(); // Delete files _fileSystem.DeleteFile(_jsonFile); _fileSystem.DeleteFile(_iniFile); _fileSystem.DeleteFile(_xmlFile); await Task.Delay(3300); Assert.Null(config["JsonKey1"]); Assert.Null(config["IniKey1"]); Assert.Null(config["XmlKey1"]); Assert.True(token.HasChanged); } [Fact] public async Task CreatingWritingDeletingCreatingFileWillReload() { // Arrange var config = CreateBuilder() .AddIniFile(_iniFile, optional: true, reloadOnChange: true) .AddJsonFile(_jsonFile, optional: true, reloadOnChange: true) .AddXmlFile(_xmlFile, optional: true, reloadOnChange: true) .Build(); Assert.Null(config["JsonKey1"]); Assert.Null(config["IniKey1"]); Assert.Null(config["XmlKey1"]); var createToken = config.GetReloadToken(); _fileSystem.WriteFile(_jsonFile, @"{""JsonKey1"": ""JsonValue1""}"); _fileSystem.WriteFile(_iniFile, @"IniKey1 = IniValue1"); _fileSystem.WriteFile(_xmlFile, @""); await Task.Delay(2000); Assert.Equal("JsonValue1", config["JsonKey1"]); Assert.Equal("IniValue1", config["IniKey1"]); Assert.Equal("XmlValue1", config["XmlKey1"]); Assert.True(createToken.HasChanged); var writeToken = config.GetReloadToken(); _fileSystem.WriteFile(_jsonFile, @"{""JsonKey1"": ""JsonValue2""}"); _fileSystem.WriteFile(_iniFile, @"IniKey1 = IniValue2"); _fileSystem.WriteFile(_xmlFile, @""); await Task.Delay(2000); Assert.Equal("JsonValue2", config["JsonKey1"]); Assert.Equal("IniValue2", config["IniKey1"]); Assert.Equal("XmlValue2", config["XmlKey1"]); Assert.True(writeToken.HasChanged); var deleteToken = config.GetReloadToken(); // Act & Assert // Delete files _fileSystem.DeleteFile(_jsonFile); _fileSystem.DeleteFile(_iniFile); _fileSystem.DeleteFile(_xmlFile); await Task.Delay(2000); Assert.Null(config["JsonKey1"]); Assert.Null(config["IniKey1"]); Assert.Null(config["XmlKey1"]); Assert.True(deleteToken.HasChanged); var createAgainToken = config.GetReloadToken(); _fileSystem.WriteFile(_jsonFile, @"{""JsonKey1"": ""JsonValue1""}"); _fileSystem.WriteFile(_iniFile, @"IniKey1 = IniValue1"); _fileSystem.WriteFile(_xmlFile, @""); await Task.Delay(2000); Assert.Equal("JsonValue1", config["JsonKey1"]); Assert.Equal("IniValue1", config["IniKey1"]); Assert.Equal("XmlValue1", config["XmlKey1"]); Assert.True(createAgainToken.HasChanged); } [Fact] public void LoadIncorrectJsonFile_ThrowFormatException() { // Arrange var json = @"{ 'name': 'test', 'address': { 'street': 'Something street' /*Missing comma*/ 'zipcode': '12345' } }"; _fileSystem.WriteFile(_jsonFile, json); // Act & Assert var exception = Assert.Throws(() => CreateBuilder().AddJsonFile(_jsonFile).Build()); Assert.NotNull(exception.Message); } [Fact] public void SetBasePathCalledMultipleTimesForEachSourceLastOneWins() { // Arrange var builder = new ConfigurationBuilder(); var jsonConfigFilePath = "test.json"; File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), jsonConfigFilePath), _jsonConfigFileContent); var xmlConfigFilePath = "test.xml"; File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), xmlConfigFilePath), _xmlConfigFileContent); // Act builder.AddXmlFile("test.xml") .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("test.json"); var config = builder.Build(); // Assert Assert.Equal("JsonValue1", config["JsonKey1"]); Assert.Equal("JsonValue2", config["Json.Key2:JsonKey3"]); Assert.Equal("JsonValue3", config["Json.Key2:Json.Key4"]); Assert.Equal("JsonValue4", config["Json.Key2:JsonKey5:JsonKey6"]); Assert.Equal("JsonValue5", config["CommonKey1:CommonKey2:JsonKey7"]); Assert.Equal("XmlValue1", config["XmlKey1"]); Assert.Equal("XmlValue2", config["XmlKey2:XmlKey3"]); Assert.Equal("XmlValue3", config["XmlKey2:XmlKey4"]); Assert.Equal("XmlValue4", config["XmlKey2:XmlKey5:XmlKey6"]); File.Delete(jsonConfigFilePath); } [Fact] public void GetDefaultBasePathForSources() { // Arrange var builder = new ConfigurationBuilder(); var jsonConfigFilePath = Path.Combine(_basePath, "test.json"); var xmlConfigFilePath = Path.Combine(_basePath, "xmltest.xml"); File.WriteAllText(jsonConfigFilePath, _jsonConfigFileContent); File.WriteAllText(xmlConfigFilePath, _xmlConfigFileContent); // Act builder.AddJsonFile("test.json").AddXmlFile("xmltest.xml"); var config = builder.Build(); // Assert Assert.Equal("JsonValue1", config["JsonKey1"]); Assert.Equal("JsonValue2", config["Json.Key2:JsonKey3"]); Assert.Equal("JsonValue3", config["Json.Key2:Json.Key4"]); Assert.Equal("JsonValue4", config["Json.Key2:JsonKey5:JsonKey6"]); Assert.Equal("JsonValue5", config["CommonKey1:CommonKey2:JsonKey7"]); Assert.Equal("XmlValue1", config["XmlKey1"]); Assert.Equal("XmlValue2", config["XmlKey2:XmlKey3"]); Assert.Equal("XmlValue3", config["XmlKey2:XmlKey4"]); Assert.Equal("XmlValue4", config["XmlKey2:XmlKey5:XmlKey6"]); File.Delete(jsonConfigFilePath); File.Delete(xmlConfigFilePath); } [Fact] public void CanEnumerateProviders() { // Arrange var config = CreateBuilder() .AddIniFile(_iniFile, optional: true, reloadOnChange: true) .AddJsonFile(_jsonFile, optional: true, reloadOnChange: true) .AddXmlFile(_xmlFile, optional: true, reloadOnChange: true) .Build(); var providers = config.Providers; Assert.Equal(3, providers.Count()); Assert.NotNull(providers.Single(p => p is JsonConfigurationProvider)); Assert.NotNull(providers.Single(p => p is XmlConfigurationProvider)); Assert.NotNull(providers.Single(p => p is IniConfigurationProvider)); } public void Dispose() { _fileProvider.Dispose(); _fileSystem.Dispose(); } } } ================================================ FILE: test/Config.FunctionalTests/DisposableFileSystem.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; namespace Microsoft.Extensions.Configuration.Test { public class DisposableFileSystem : IDisposable { public DisposableFileSystem() { RootPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Directory.CreateDirectory(RootPath); DirectoryInfo = new DirectoryInfo(RootPath); } public string RootPath { get; } public DirectoryInfo DirectoryInfo { get; } public DisposableFileSystem CreateFolder(string path) { Directory.CreateDirectory(Path.Combine(RootPath, path)); return this; } public DisposableFileSystem WriteFile(string path, string text = "temp") { File.WriteAllText(Path.Combine(RootPath, path), text); return this; } public DisposableFileSystem DeleteFile(string path) { File.Delete(Path.Combine(RootPath, path)); return this; } public DisposableFileSystem CreateFiles(params string[] fileRelativePaths) { foreach (var path in fileRelativePaths) { var fullPath = Path.Combine(RootPath, path); Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); File.WriteAllText( fullPath, string.Format("Automatically generated for testing on {0:yyyy}/{0:MM}/{0:dd} {0:hh}:{0:mm}:{0:ss}", DateTime.UtcNow)); } return this; } public void Dispose() { try { Directory.Delete(RootPath, true); } catch { // Don't throw if this fails. } } } } ================================================ FILE: test/Config.FunctionalTests/test.xml ================================================ XmlValue3 XmlValue4 ================================================ FILE: test/Config.Ini.Test/Config.Ini.Test.csproj ================================================  Microsoft.Extensions.Configuration.Ini.Test Microsoft.Extensions.Configuration.Ini.Test $(StandardTestTfms) ================================================ FILE: test/Config.Ini.Test/IniConfigurationExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using Xunit; namespace Microsoft.Extensions.Configuration.Ini.Test { public class IniConfigurationExtensionsTest { [Theory] [InlineData(null)] [InlineData("")] public void AddIniFile_ThrowsIfFilePathIsNullOrEmpty(string path) { // Arrange var configurationBuilder = new ConfigurationBuilder(); // Act and Assert var ex = Assert.Throws( () => IniConfigurationExtensions.AddIniFile(configurationBuilder, path)); Assert.Equal("path", ex.ParamName); Assert.StartsWith("File path must be a non-empty string.", ex.Message); } [Fact] public void AddIniFile_ThrowsIfFileDoesNotExistAtPath() { // Arrange var path = "file-does-not-exist.ini"; // Act and Assert var ex = Assert.Throws(() => new ConfigurationBuilder().AddIniFile(path).Build()); Assert.StartsWith($"The configuration file '{path}' was not found and is not optional. The physical path is '", ex.Message); } [Fact] public void AddIniFile_DoesNotThrowsIfFileDoesNotExistAtPathAndOptional() { // Arrange var path = "file-does-not-exist.ini"; // Act and Assert new ConfigurationBuilder().AddIniFile(path, optional: true).Build(); } } } ================================================ FILE: test/Config.Ini.Test/IniConfigurationTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using Microsoft.Extensions.Configuration.Test; using Xunit; namespace Microsoft.Extensions.Configuration.Ini.Test { public class IniConfigurationTest { [Fact] public void LoadKeyValuePairsFromValidIniFile() { var ini = @"[DefaultConnection] ConnectionString=TestConnectionString Provider=SqlClient [Data:Inventory] ConnectionString=AnotherTestConnectionString SubHeader:Provider=MySql"; var iniConfigSrc = new IniConfigurationProvider(new IniConfigurationSource()); iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("TestConnectionString", iniConfigSrc.Get("defaultconnection:ConnectionString")); Assert.Equal("SqlClient", iniConfigSrc.Get("DEFAULTCONNECTION:PROVIDER")); Assert.Equal("AnotherTestConnectionString", iniConfigSrc.Get("Data:Inventory:CONNECTIONSTRING")); Assert.Equal("MySql", iniConfigSrc.Get("Data:Inventory:SubHeader:Provider")); } [Fact] public void LoadMethodCanHandleEmptyValue() { var ini = @"DefaultKey="; var iniConfigSrc = new IniConfigurationProvider(new IniConfigurationSource()); iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal(string.Empty, iniConfigSrc.Get("DefaultKey")); } [Fact] public void LoadKeyValuePairsFromValidIniFileWithQuotedValues() { var ini = "[DefaultConnection]\n" + "ConnectionString=\"TestConnectionString\"\n" + "Provider=\"SqlClient\"\n" + "[Data:Inventory]\n" + "ConnectionString=\"AnotherTestConnectionString\"\n" + "Provider=\"MySql\""; var iniConfigSrc = new IniConfigurationProvider(new IniConfigurationSource()); iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("TestConnectionString", iniConfigSrc.Get("DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", iniConfigSrc.Get("DefaultConnection:Provider")); Assert.Equal("AnotherTestConnectionString", iniConfigSrc.Get("Data:Inventory:ConnectionString")); Assert.Equal("MySql", iniConfigSrc.Get("Data:Inventory:Provider")); } [Fact] public void DoubleQuoteIsPartOfValueIfNotPaired() { var ini = "[ConnectionString]\n" + "DefaultConnection=\"TestConnectionString\n" + "Provider=SqlClient\""; var iniConfigSrc = new IniConfigurationProvider(new IniConfigurationSource()); iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("\"TestConnectionString", iniConfigSrc.Get("ConnectionString:DefaultConnection")); Assert.Equal("SqlClient\"", iniConfigSrc.Get("ConnectionString:Provider")); } [Fact] public void DoubleQuoteIsPartOfValueIfAppearInTheMiddleOfValue() { var ini = "[ConnectionString]\n" + "DefaultConnection=Test\"Connection\"String\n" + "Provider=Sql\"Client"; var iniConfigSrc = new IniConfigurationProvider(new IniConfigurationSource()); iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("Test\"Connection\"String", iniConfigSrc.Get("ConnectionString:DefaultConnection")); Assert.Equal("Sql\"Client", iniConfigSrc.Get("ConnectionString:Provider")); } [Fact] public void LoadKeyValuePairsFromValidIniFileWithoutSectionHeader() { var ini = @" DefaultConnection:ConnectionString=TestConnectionString DefaultConnection:Provider=SqlClient Data:Inventory:ConnectionString=AnotherTestConnectionString Data:Inventory:Provider=MySql "; var iniConfigSrc = new IniConfigurationProvider(new IniConfigurationSource()); iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("TestConnectionString", iniConfigSrc.Get("DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", iniConfigSrc.Get("DefaultConnection:Provider")); Assert.Equal("AnotherTestConnectionString", iniConfigSrc.Get("Data:Inventory:ConnectionString")); Assert.Equal("MySql", iniConfigSrc.Get("Data:Inventory:Provider")); } [Fact] public void SupportAndIgnoreComments() { var ini = @" ; Comments [DefaultConnection] # Comments ConnectionString=TestConnectionString / Comments Provider=SqlClient [Data:Inventory] ConnectionString=AnotherTestConnectionString Provider=MySql "; var iniConfigSrc = new IniConfigurationProvider(new IniConfigurationSource()); iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini)); Assert.Equal("TestConnectionString", iniConfigSrc.Get("DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", iniConfigSrc.Get("DefaultConnection:Provider")); Assert.Equal("AnotherTestConnectionString", iniConfigSrc.Get("Data:Inventory:ConnectionString")); Assert.Equal("MySql", iniConfigSrc.Get("Data:Inventory:Provider")); } [Fact] public void ThrowExceptionWhenFoundInvalidLine() { var ini = @" ConnectionString "; var iniConfigSrc = new IniConfigurationProvider(new IniConfigurationSource()); var expectedMsg = Resources.FormatError_UnrecognizedLineFormat("ConnectionString"); var exception = Assert.Throws(() => iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini))); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void ThrowExceptionWhenFoundBrokenSectionHeader() { var ini = @" [ConnectionString DefaultConnection=TestConnectionString "; var iniConfigSrc = new IniConfigurationProvider(new IniConfigurationSource()); var expectedMsg = Resources.FormatError_UnrecognizedLineFormat("[ConnectionString"); var exception = Assert.Throws(() => iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini))); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void ThrowExceptionWhenPassingNullAsFilePath() { var expectedMsg = new ArgumentException(Resources.Error_InvalidFilePath, "path").Message; var exception = Assert.Throws(() => new ConfigurationBuilder().AddIniFile(path: null)); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void ThrowExceptionWhenPassingEmptyStringAsFilePath() { var expectedMsg = new ArgumentException(Resources.Error_InvalidFilePath, "path").Message; var exception = Assert.Throws(() => new ConfigurationBuilder().AddIniFile(string.Empty)); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void ThrowExceptionWhenKeyIsDuplicated() { var ini = @" [Data:DefaultConnection] ConnectionString=TestConnectionString Provider=SqlClient [Data] DefaultConnection:ConnectionString=AnotherTestConnectionString Provider=MySql "; var iniConfigSrc = new IniConfigurationProvider(new IniConfigurationSource()); var expectedMsg = Resources.FormatError_KeyIsDuplicated("Data:DefaultConnection:ConnectionString"); var exception = Assert.Throws(() => iniConfigSrc.Load(TestStreamHelpers.StringToStream(ini))); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void IniConfiguration_Throws_On_Missing_Configuration_File() { var exception = Assert.Throws(() => new ConfigurationBuilder().AddIniFile("NotExistingConfig.ini").Build()); // Assert Assert.StartsWith($"The configuration file 'NotExistingConfig.ini' was not found and is not optional. The physical path is '", exception.Message); } [Fact] public void IniConfiguration_Does_Not_Throw_On_Optional_Configuration() { var config = new ConfigurationBuilder().AddIniFile("NotExistingConfig.ini", optional: true).Build(); } } } ================================================ FILE: test/Config.Json.Test/ArrayTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using Microsoft.Extensions.Configuration.Test; using Xunit; namespace Microsoft.Extensions.Configuration.Json.Test { public class ArrayTest { [Fact] public void ArraysAreConvertedToKeyValuePairs() { var json = @"{ 'ip': [ '1.2.3.4', '7.8.9.10', '11.12.13.14' ] }"; var jsonConfigSource = new JsonConfigurationProvider(new JsonConfigurationSource()); jsonConfigSource.Load(TestStreamHelpers.StringToStream(json)); Assert.Equal("1.2.3.4", jsonConfigSource.Get("ip:0")); Assert.Equal("7.8.9.10", jsonConfigSource.Get("ip:1")); Assert.Equal("11.12.13.14", jsonConfigSource.Get("ip:2")); } [Fact] public void ArrayOfObjects() { var json = @"{ 'ip': [ { 'address': '1.2.3.4', 'hidden': false }, { 'address': '5.6.7.8', 'hidden': true } ] }"; var jsonConfigSource = new JsonConfigurationProvider(new JsonConfigurationSource()); jsonConfigSource.Load(TestStreamHelpers.StringToStream(json)); Assert.Equal("1.2.3.4", jsonConfigSource.Get("ip:0:address")); Assert.Equal("False", jsonConfigSource.Get("ip:0:hidden")); Assert.Equal("5.6.7.8", jsonConfigSource.Get("ip:1:address")); Assert.Equal("True", jsonConfigSource.Get("ip:1:hidden")); } [Fact] public void NestedArrays() { var json = @"{ 'ip': [ [ '1.2.3.4', '5.6.7.8' ], [ '9.10.11.12', '13.14.15.16' ], ] }"; var jsonConfigSource = new JsonConfigurationProvider(new JsonConfigurationSource()); jsonConfigSource.Load(TestStreamHelpers.StringToStream(json)); Assert.Equal("1.2.3.4", jsonConfigSource.Get("ip:0:0")); Assert.Equal("5.6.7.8", jsonConfigSource.Get("ip:0:1")); Assert.Equal("9.10.11.12", jsonConfigSource.Get("ip:1:0")); Assert.Equal("13.14.15.16", jsonConfigSource.Get("ip:1:1")); } [Fact] public void ImplicitArrayItemReplacement() { var json1 = @"{ 'ip': [ '1.2.3.4', '7.8.9.10', '11.12.13.14' ] }"; var json2 = @"{ 'ip': [ '15.16.17.18' ] }"; var jsonConfigSource1 = new JsonConfigurationSource { FileProvider = TestStreamHelpers.StringToFileProvider(json1) }; var jsonConfigSource2 = new JsonConfigurationSource { FileProvider = TestStreamHelpers.StringToFileProvider(json2) }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.Add(jsonConfigSource1); configurationBuilder.Add(jsonConfigSource2); var config = configurationBuilder.Build(); Assert.Equal(3, config.GetSection("ip").GetChildren().Count()); Assert.Equal("15.16.17.18", config["ip:0"]); Assert.Equal("7.8.9.10", config["ip:1"]); Assert.Equal("11.12.13.14", config["ip:2"]); } [Fact] public void ExplicitArrayReplacement() { var json1 = @"{ 'ip': [ '1.2.3.4', '7.8.9.10', '11.12.13.14' ] }"; var json2 = @"{ 'ip': { '1': '15.16.17.18' } }"; var jsonConfigSource1 = new JsonConfigurationSource { FileProvider = TestStreamHelpers.StringToFileProvider(json1) }; var jsonConfigSource2 = new JsonConfigurationSource { FileProvider = TestStreamHelpers.StringToFileProvider(json2) }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.Add(jsonConfigSource1); configurationBuilder.Add(jsonConfigSource2); var config = configurationBuilder.Build(); Assert.Equal(3, config.GetSection("ip").GetChildren().Count()); Assert.Equal("1.2.3.4", config["ip:0"]); Assert.Equal("15.16.17.18", config["ip:1"]); Assert.Equal("11.12.13.14", config["ip:2"]); } [Fact] public void ArrayMerge() { var json1 = @"{ 'ip': [ '1.2.3.4', '7.8.9.10', '11.12.13.14' ] }"; var json2 = @"{ 'ip': { '3': '15.16.17.18' } }"; var jsonConfigSource1 = new JsonConfigurationSource { FileProvider = TestStreamHelpers.StringToFileProvider(json1) }; var jsonConfigSource2 = new JsonConfigurationSource { FileProvider = TestStreamHelpers.StringToFileProvider(json2) }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.Add(jsonConfigSource1); configurationBuilder.Add(jsonConfigSource2); var config = configurationBuilder.Build(); Assert.Equal(4, config.GetSection("ip").GetChildren().Count()); Assert.Equal("1.2.3.4", config["ip:0"]); Assert.Equal("7.8.9.10", config["ip:1"]); Assert.Equal("11.12.13.14", config["ip:2"]); Assert.Equal("15.16.17.18", config["ip:3"]); } [Fact] public void ArraysAreKeptInFileOrder() { var json = @"{ 'setting': [ 'b', 'a', '2' ] }"; var jsonConfigSource = new JsonConfigurationSource { FileProvider = TestStreamHelpers.StringToFileProvider(json) }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.Add(jsonConfigSource); var config = configurationBuilder.Build(); var configurationSection = config.GetSection("setting"); var indexConfigurationSections = configurationSection.GetChildren().ToArray(); Assert.Equal(3, indexConfigurationSections.Count()); Assert.Equal("b", indexConfigurationSections[0].Value); Assert.Equal("a", indexConfigurationSections[1].Value); Assert.Equal("2", indexConfigurationSections[2].Value); } [Fact] public void PropertiesAreSortedByNumberOnlyFirst() { var json = @"{ 'setting': { 'hello': 'a', 'bob': 'b', '42': 'c', '4':'d', '10': 'e', '1text': 'f', } }"; var jsonConfigSource = new JsonConfigurationSource { FileProvider = TestStreamHelpers.StringToFileProvider(json) }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.Add(jsonConfigSource); var config = configurationBuilder.Build(); var configurationSection = config.GetSection("setting"); var indexConfigurationSections = configurationSection.GetChildren().ToArray(); Assert.Equal(6, indexConfigurationSections.Count()); Assert.Equal("4", indexConfigurationSections[0].Key); Assert.Equal("10", indexConfigurationSections[1].Key); Assert.Equal("42", indexConfigurationSections[2].Key); Assert.Equal("1text", indexConfigurationSections[3].Key); Assert.Equal("bob", indexConfigurationSections[4].Key); Assert.Equal("hello", indexConfigurationSections[5].Key); } } } ================================================ FILE: test/Config.Json.Test/Config.Json.Test.csproj ================================================  Microsoft.Extensions.Configuration.Json.Test Microsoft.Extensions.Configuration.Json.Test $(StandardTestTfms) ================================================ FILE: test/Config.Json.Test/JsonConfigurationExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using Xunit; namespace Microsoft.Extensions.Configuration.Json { public class JsonConfigurationExtensionsTest { [Theory] [InlineData(null)] [InlineData("")] public void AddJsonFile_ThrowsIfFilePathIsNullOrEmpty(string path) { // Arrange var configurationBuilder = new ConfigurationBuilder(); // Act and Assert var ex = Assert.Throws(() => JsonConfigurationExtensions.AddJsonFile(configurationBuilder, path)); Assert.Equal("path", ex.ParamName); Assert.StartsWith("File path must be a non-empty string.", ex.Message); } [Fact] public void AddJsonFile_ThrowsIfFileDoesNotExistAtPath() { // Arrange var path = "file-does-not-exist.json"; // Act and Assert var ex = Assert.Throws(() => new ConfigurationBuilder().AddJsonFile(path).Build()); Assert.StartsWith($"The configuration file '{path}' was not found and is not optional. The physical path is '", ex.Message); } } } ================================================ FILE: test/Config.Json.Test/JsonConfigurationTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.IO; using Microsoft.Extensions.Configuration.Json; using Microsoft.Extensions.Configuration.Test; using Xunit; namespace Microsoft.Extensions.Configuration { public class JsonConfigurationTest { private JsonConfigurationProvider LoadProvider(string json) { var p = new JsonConfigurationProvider(new JsonConfigurationSource { Optional = true }); p.Load(TestStreamHelpers.StringToStream(json)); return p; } [Fact] public void LoadKeyValuePairsFromValidJson() { var json = @" { 'firstname': 'test', 'test.last.name': 'last.name', 'residential.address': { 'street.name': 'Something street', 'zipcode': '12345' } }"; var jsonConfigSrc = LoadProvider(json); Assert.Equal("test", jsonConfigSrc.Get("firstname")); Assert.Equal("last.name", jsonConfigSrc.Get("test.last.name")); Assert.Equal("Something street", jsonConfigSrc.Get("residential.address:STREET.name")); Assert.Equal("12345", jsonConfigSrc.Get("residential.address:zipcode")); } [Fact] public void LoadMethodCanHandleEmptyValue() { var json = @" { 'name': '' }"; var jsonConfigSrc = LoadProvider(json); Assert.Equal(string.Empty, jsonConfigSrc.Get("name")); } [Fact] public void LoadWithCulture() { var previousCulture = CultureInfo.CurrentCulture; try { CultureInfo.CurrentCulture = new CultureInfo("fr-FR"); var json = @" { 'number': 3.14 }"; var jsonConfigSrc = LoadProvider(json); Assert.Equal("3.14", jsonConfigSrc.Get("number")); } finally { CultureInfo.CurrentCulture = previousCulture; } } [Fact] public void NonObjectRootIsInvalid() { var json = @"'test'"; var exception = Assert.Throws( () => LoadProvider(json)); Assert.NotNull(exception.Message); } [Fact] public void SupportAndIgnoreComments() { var json = @"/* Comments */ {/* Comments */ ""name"": /* Comments */ ""test"", ""address"": { ""street"": ""Something street"", /* Comments */ ""zipcode"": ""12345"" } }"; var jsonConfigSrc = LoadProvider(json); Assert.Equal("test", jsonConfigSrc.Get("name")); Assert.Equal("Something street", jsonConfigSrc.Get("address:street")); Assert.Equal("12345", jsonConfigSrc.Get("address:zipcode")); } [Fact] public void ThrowExceptionWhenUnexpectedEndFoundBeforeFinishParsing() { var json = @"{ 'name': 'test', 'address': { 'street': 'Something street', 'zipcode': '12345' } /* Missing a right brace here*/"; var exception = Assert.Throws(() => LoadProvider(json)); Assert.NotNull(exception.Message); } [Fact] public void ThrowExceptionWhenMissingCurlyBeforeFinishParsing() { var json = @" { 'Data': { "; var exception = Assert.Throws(() => LoadProvider(json)); Assert.Contains("Could not parse the JSON file.", exception.Message); } [Fact] public void ThrowExceptionWhenPassingNullAsFilePath() { var expectedMsg = new ArgumentException(Resources.Error_InvalidFilePath, "path").Message; var exception = Assert.Throws(() => new ConfigurationBuilder().AddJsonFile(path: null)); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void ThrowExceptionWhenPassingEmptyStringAsFilePath() { var expectedMsg = new ArgumentException(Resources.Error_InvalidFilePath, "path").Message; var exception = Assert.Throws(() => new ConfigurationBuilder().AddJsonFile(string.Empty)); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void JsonConfiguration_Throws_On_Missing_Configuration_File() { var config = new ConfigurationBuilder().AddJsonFile("NotExistingConfig.json", optional: false); var exception = Assert.Throws(() => config.Build()); // Assert Assert.StartsWith($"The configuration file 'NotExistingConfig.json' was not found and is not optional. The physical path is '", exception.Message); } [Fact] public void JsonConfiguration_Does_Not_Throw_On_Optional_Configuration() { var config = new ConfigurationBuilder().AddJsonFile("NotExistingConfig.json", optional: true).Build(); } [Fact] public void ThrowFormatExceptionWhenFileIsEmpty() { var exception = Assert.Throws(() => LoadProvider(@"")); } } } ================================================ FILE: test/Config.KeyPerFile.Test/Config.KeyPerFile.Test.csproj ================================================  Microsoft.Extensions.Configuration.KeyPerFile.Test Microsoft.Extensions.Configuration.KeyPerFile.Test $(StandardTestTfms) ================================================ FILE: test/Config.KeyPerFile.Test/KeyPerFileTests.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; using Xunit; namespace Microsoft.Extensions.Configuration.KeyPerFile.Test { public class KeyPerFileTests { [Fact] public void DoesNotThrowWhenOptionalAndNoSecrets() { new ConfigurationBuilder().AddKeyPerFile(o => o.Optional = true).Build(); } [Fact] public void DoesNotThrowWhenOptionalAndDirectoryDoesntExist() { new ConfigurationBuilder().AddKeyPerFile("nonexistent", true).Build(); } [Fact] public void ThrowsWhenNotOptionalAndDirectoryDoesntExist() { var e = Assert.Throws(() => new ConfigurationBuilder().AddKeyPerFile("nonexistent", false).Build()); Assert.Contains("The path must be absolute.", e.Message); } [Fact] public void CanLoadMultipleSecrets() { var testFileProvider = new TestFileProvider( new TestFile("Secret1", "SecretValue1"), new TestFile("Secret2", "SecretValue2")); var config = new ConfigurationBuilder() .AddKeyPerFile(o => o.FileProvider = testFileProvider) .Build(); Assert.Equal("SecretValue1", config["Secret1"]); Assert.Equal("SecretValue2", config["Secret2"]); } [Fact] public void CanLoadMultipleSecretsWithDirectory() { var testFileProvider = new TestFileProvider( new TestFile("Secret1", "SecretValue1"), new TestFile("Secret2", "SecretValue2"), new TestFile("directory")); var config = new ConfigurationBuilder() .AddKeyPerFile(o => o.FileProvider = testFileProvider) .Build(); Assert.Equal("SecretValue1", config["Secret1"]); Assert.Equal("SecretValue2", config["Secret2"]); } [Fact] public void CanLoadNestedKeys() { var testFileProvider = new TestFileProvider( new TestFile("Secret0__Secret1__Secret2__Key", "SecretValue2"), new TestFile("Secret0__Secret1__Key", "SecretValue1"), new TestFile("Secret0__Key", "SecretValue0")); var config = new ConfigurationBuilder() .AddKeyPerFile(o => o.FileProvider = testFileProvider) .Build(); Assert.Equal("SecretValue0", config["Secret0:Key"]); Assert.Equal("SecretValue1", config["Secret0:Secret1:Key"]); Assert.Equal("SecretValue2", config["Secret0:Secret1:Secret2:Key"]); } [Fact] public void CanIgnoreFilesWithDefault() { var testFileProvider = new TestFileProvider( new TestFile("ignore.Secret0", "SecretValue0"), new TestFile("ignore.Secret1", "SecretValue1"), new TestFile("Secret2", "SecretValue2")); var config = new ConfigurationBuilder() .AddKeyPerFile(o => o.FileProvider = testFileProvider) .Build(); Assert.Null(config["ignore.Secret0"]); Assert.Null(config["ignore.Secret1"]); Assert.Equal("SecretValue2", config["Secret2"]); } [Fact] public void CanTurnOffDefaultIgnorePrefixWithCondition() { var testFileProvider = new TestFileProvider( new TestFile("ignore.Secret0", "SecretValue0"), new TestFile("ignore.Secret1", "SecretValue1"), new TestFile("Secret2", "SecretValue2")); var config = new ConfigurationBuilder() .AddKeyPerFile(o => { o.FileProvider = testFileProvider; o.IgnoreCondition = null; }) .Build(); Assert.Equal("SecretValue0", config["ignore.Secret0"]); Assert.Equal("SecretValue1", config["ignore.Secret1"]); Assert.Equal("SecretValue2", config["Secret2"]); } [Fact] public void CanIgnoreAllWithCondition() { var testFileProvider = new TestFileProvider( new TestFile("Secret0", "SecretValue0"), new TestFile("Secret1", "SecretValue1"), new TestFile("Secret2", "SecretValue2")); var config = new ConfigurationBuilder() .AddKeyPerFile(o => { o.FileProvider = testFileProvider; o.IgnoreCondition = s => true; }) .Build(); Assert.Empty(config.AsEnumerable()); } [Fact] public void CanIgnoreFilesWithCustomIgnore() { var testFileProvider = new TestFileProvider( new TestFile("meSecret0", "SecretValue0"), new TestFile("meSecret1", "SecretValue1"), new TestFile("Secret2", "SecretValue2")); var config = new ConfigurationBuilder() .AddKeyPerFile(o => { o.FileProvider = testFileProvider; o.IgnorePrefix = "me"; }) .Build(); Assert.Null(config["meSecret0"]); Assert.Null(config["meSecret1"]); Assert.Equal("SecretValue2", config["Secret2"]); } [Fact] public void CanUnIgnoreDefaultFiles() { var testFileProvider = new TestFileProvider( new TestFile("ignore.Secret0", "SecretValue0"), new TestFile("ignore.Secret1", "SecretValue1"), new TestFile("Secret2", "SecretValue2")); var config = new ConfigurationBuilder() .AddKeyPerFile(o => { o.FileProvider = testFileProvider; o.IgnorePrefix = null; }) .Build(); Assert.Equal("SecretValue0", config["ignore.Secret0"]); Assert.Equal("SecretValue1", config["ignore.Secret1"]); Assert.Equal("SecretValue2", config["Secret2"]); } } class TestFileProvider : IFileProvider { IDirectoryContents _contents; public TestFileProvider(params IFileInfo[] files) { _contents = new TestDirectoryContents(files); } public IDirectoryContents GetDirectoryContents(string subpath) { return _contents; } public IFileInfo GetFileInfo(string subpath) { throw new NotImplementedException(); } public IChangeToken Watch(string filter) { throw new NotImplementedException(); } } class TestDirectoryContents : IDirectoryContents { List _list; public TestDirectoryContents(params IFileInfo[] files) { _list = new List(files); } public bool Exists { get { return true; } } public IEnumerator GetEnumerator() { return _list.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } //TODO: Probably need a directory and file type. class TestFile : IFileInfo { private string _name; private string _contents; public bool Exists { get { return true; } } public bool IsDirectory { get; } public DateTimeOffset LastModified { get { throw new NotImplementedException(); } } public long Length { get { throw new NotImplementedException(); } } public string Name { get { return _name; } } public string PhysicalPath { get { throw new NotImplementedException(); } } public TestFile(string name) { _name = name; IsDirectory = true; } public TestFile(string name, string contents) { _name = name; _contents = contents; } public Stream CreateReadStream() { if(IsDirectory) { throw new InvalidOperationException("Cannot create stream from directory"); } return new MemoryStream(Encoding.UTF8.GetBytes(_contents)); } } } ================================================ FILE: test/Config.Test/Config.Test.csproj ================================================  Microsoft.Extensions.Configuration.Test Microsoft.Extensions.Configuration.Test $(StandardTestTfms) ================================================ FILE: test/Config.Test/ConfigurationPathComparerTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Xunit; namespace Microsoft.Extensions.Configuration.Test { public class ConfigurationPathComparerTest { [Fact] public void CompareWithNull() { ComparerTest(null, null, 0); ComparerTest(null, "a", -1); ComparerTest("b", null, 1); } [Fact] public void CompareWithSameLength() { ComparerTest("a", "a", 0); ComparerTest("a", "A", 0); ComparerTest("aB", "Ab", 0); } [Fact] public void CompareWithDifferentLengths() { ComparerTest("a", "aa", -1); ComparerTest("aa", "a", 1); } [Fact] public void CompareWithLetters() { ComparerTest("a", "b", -1); ComparerTest("b", "a", 1); } [Fact] public void CompareWithNumbers() { ComparerTest("000", "0", 0); ComparerTest("001", "1", 0); ComparerTest("1", "1", 0); ComparerTest("1", "10", -1); ComparerTest("10", "1", 1); ComparerTest("2", "10", -1); ComparerTest("10", "2", 1); } [Fact] public void CompareWithNumbersAndLetters() { ComparerTest("1", "a", -1); ComparerTest("a", "1", 1); ComparerTest("100", "a", -1); ComparerTest("a", "100", 1); } [Fact] public void CompareWithNonNumbers() { ComparerTest("1a", "100", 1); ComparerTest("100", "1a", -1); ComparerTest("100a", "100", 1); ComparerTest("100", "100a", -1); ComparerTest("a100", "100", 1); ComparerTest("100", "a100", -1); ComparerTest("1a", "a", -1); ComparerTest("a", "1a", 1); } [Fact] public void CompareIdenticalPaths() { ComparerTest("abc:DEF:0:a100", "ABC:DEF:0:a100", 0); } [Fact] public void CompareDifferentPaths() { ComparerTest("abc:def", "ghi:2", -1); ComparerTest("ghi:2", "abc:def", 1); } [Fact] public void ComparePathsWithCommonPart() { ComparerTest("abc:def:XYQ", "abc:def:XYZ", -1); ComparerTest("abc:def:XYZ", "abc:def:XYQ", 1); } [Fact] public void ComparePathsWithCommonPartButShorter() { ComparerTest("abc:def", "abc:def:ghi", -1); ComparerTest("abc:def:ghi", "abc:def", 1); } [Fact] public void ComparePathsWithIndicesAtTheEnd() { ComparerTest("abc:def:2", "abc:def:10", -1); ComparerTest("abc:def:10", "abc:def:2", 1); ComparerTest("abc:def:10", "abc:def:22", -1); ComparerTest("abc:def:22", "abc:def:10", 1); } [Fact] public void ComparePathsWithIndicesInside() { ComparerTest("abc:def:1000:jkl", "abc:def:ghi:jkl", -1); ComparerTest("abc:def:ghi:jkl", "abc:def:1000:jkl", 1); ComparerTest("abc:def:10:jkl", "abc:def:22:jkl", -1); ComparerTest("abc:def:22:jkl", "abc:def:10:jkl", 1); } private static void ComparerTest(string a, string b, int expectedSign) { var result = ConfigurationKeyComparer.Instance.Compare(a, b); Assert.Equal(expectedSign, Math.Sign(result)); } } } ================================================ FILE: test/Config.Test/ConfigurationPathTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Xunit; namespace Microsoft.Extensions.Configuration.Test { public class ConfigurationPathTest { [Fact] public void CombineWithEmptySegmentLeavesDelimiter() { Assert.Equal("parent:", ConfigurationPath.Combine("parent", "")); Assert.Equal("parent::", ConfigurationPath.Combine("parent", "", "")); Assert.Equal("parent:::key", ConfigurationPath.Combine("parent", "", "", "key")); } [Fact] public void GetLastSegmenGetSectionKeyTests() { Assert.Null(ConfigurationPath.GetSectionKey(null)); Assert.Equal("", ConfigurationPath.GetSectionKey("")); Assert.Equal("", ConfigurationPath.GetSectionKey(":::")); Assert.Equal("c", ConfigurationPath.GetSectionKey("a::b:::c")); Assert.Equal("", ConfigurationPath.GetSectionKey("a:::b:")); Assert.Equal("key", ConfigurationPath.GetSectionKey("key")); Assert.Equal("key", ConfigurationPath.GetSectionKey(":key")); Assert.Equal("key", ConfigurationPath.GetSectionKey("::key")); Assert.Equal("key", ConfigurationPath.GetSectionKey("parent:key")); } [Fact] public void GetParentPathTests() { Assert.Null(ConfigurationPath.GetParentPath(null)); Assert.Null(ConfigurationPath.GetParentPath("")); Assert.Equal("::", ConfigurationPath.GetParentPath(":::")); Assert.Equal("a::b::", ConfigurationPath.GetParentPath("a::b:::c")); Assert.Equal("a:::b", ConfigurationPath.GetParentPath("a:::b:")); Assert.Null(ConfigurationPath.GetParentPath("key")); Assert.Equal("", ConfigurationPath.GetParentPath(":key")); Assert.Equal(":", ConfigurationPath.GetParentPath("::key")); Assert.Equal("parent", ConfigurationPath.GetParentPath("parent:key")); } } } ================================================ FILE: test/Config.Test/ConfigurationTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Configuration.Memory; using Xunit; namespace Microsoft.Extensions.Configuration.Test { public class ConfigurationTest { [Fact] public void LoadAndCombineKeyValuePairsFromDifferentConfigurationProviders() { // Arrange var dic1 = new Dictionary() { {"Mem1:KeyInMem1", "ValueInMem1"} }; var dic2 = new Dictionary() { {"Mem2:KeyInMem2", "ValueInMem2"} }; var dic3 = new Dictionary() { {"Mem3:KeyInMem3", "ValueInMem3"} }; var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; var configurationBuilder = new ConfigurationBuilder(); // Act configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); configurationBuilder.Add(memConfigSrc3); var config = configurationBuilder.Build(); var memVal1 = config["mem1:keyinmem1"]; var memVal2 = config["Mem2:KeyInMem2"]; var memVal3 = config["MEM3:KEYINMEM3"]; // Assert Assert.Contains(memConfigSrc1, configurationBuilder.Sources); Assert.Contains(memConfigSrc2, configurationBuilder.Sources); Assert.Contains(memConfigSrc3, configurationBuilder.Sources); Assert.Equal("ValueInMem1", memVal1); Assert.Equal("ValueInMem2", memVal2); Assert.Equal("ValueInMem3", memVal3); Assert.Equal("ValueInMem1", config["mem1:keyinmem1"]); Assert.Equal("ValueInMem2", config["Mem2:KeyInMem2"]); Assert.Equal("ValueInMem3", config["MEM3:KEYINMEM3"]); Assert.Null(config["NotExist"]); } [Fact] public void CanChainConfiguration() { // Arrange var dic1 = new Dictionary() { {"Mem1:KeyInMem1", "ValueInMem1"} }; var dic2 = new Dictionary() { {"Mem2:KeyInMem2", "ValueInMem2"} }; var dic3 = new Dictionary() { {"Mem3:KeyInMem3", "ValueInMem3"} }; var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; var configurationBuilder = new ConfigurationBuilder(); // Act configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); configurationBuilder.Add(memConfigSrc3); var config = configurationBuilder.Build(); var chained = new ConfigurationBuilder().AddConfiguration(config).Build(); var memVal1 = chained["mem1:keyinmem1"]; var memVal2 = chained["Mem2:KeyInMem2"]; var memVal3 = chained["MEM3:KEYINMEM3"]; // Assert Assert.Equal("ValueInMem1", memVal1); Assert.Equal("ValueInMem2", memVal2); Assert.Equal("ValueInMem3", memVal3); Assert.Null(chained["NotExist"]); } [Theory] [InlineData(true)] [InlineData(false)] public void ChainedAsEnumerateFlattensIntoDictionaryTest(bool removePath) { // Arrange var dic1 = new Dictionary() { {"Mem1", "Value1"}, {"Mem1:", "NoKeyValue1"}, {"Mem1:KeyInMem1", "ValueInMem1"}, {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} }; var dic2 = new Dictionary() { {"Mem2", "Value2"}, {"Mem2:", "NoKeyValue2"}, {"Mem2:KeyInMem2", "ValueInMem2"}, {"Mem2:KeyInMem2:Deep2", "ValueDeep2"} }; var dic3 = new Dictionary() { {"Mem3", "Value3"}, {"Mem3:", "NoKeyValue3"}, {"Mem3:KeyInMem3", "ValueInMem3"}, {"Mem3:KeyInMem3:Deep3", "ValueDeep3"} }; var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; var configurationBuilder = new ConfigurationBuilder(); // Act configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); var config = new ConfigurationBuilder() .AddConfiguration(configurationBuilder.Build()) .Add(memConfigSrc3) .Build(); var dict = config.AsEnumerable(makePathsRelative: removePath).ToDictionary(k => k.Key, v => v.Value); // Assert Assert.Equal("Value1", dict["Mem1"]); Assert.Equal("NoKeyValue1", dict["Mem1:"]); Assert.Equal("ValueDeep1", dict["Mem1:KeyInMem1:Deep1"]); Assert.Equal("ValueInMem2", dict["Mem2:KeyInMem2"]); Assert.Equal("Value2", dict["Mem2"]); Assert.Equal("NoKeyValue2", dict["Mem2:"]); Assert.Equal("ValueDeep2", dict["Mem2:KeyInMem2:Deep2"]); Assert.Equal("Value3", dict["Mem3"]); Assert.Equal("NoKeyValue3", dict["Mem3:"]); Assert.Equal("ValueInMem3", dict["Mem3:KeyInMem3"]); Assert.Equal("ValueDeep3", dict["Mem3:KeyInMem3:Deep3"]); } [Theory] [InlineData(true)] [InlineData(false)] public void AsEnumerateFlattensIntoDictionaryTest(bool removePath) { // Arrange var dic1 = new Dictionary() { {"Mem1", "Value1"}, {"Mem1:", "NoKeyValue1"}, {"Mem1:KeyInMem1", "ValueInMem1"}, {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} }; var dic2 = new Dictionary() { {"Mem2", "Value2"}, {"Mem2:", "NoKeyValue2"}, {"Mem2:KeyInMem2", "ValueInMem2"}, {"Mem2:KeyInMem2:Deep2", "ValueDeep2"} }; var dic3 = new Dictionary() { {"Mem3", "Value3"}, {"Mem3:", "NoKeyValue3"}, {"Mem3:KeyInMem3", "ValueInMem3"}, {"Mem3:KeyInMem3:Deep3", "ValueDeep3"} }; var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; var configurationBuilder = new ConfigurationBuilder(); // Act configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); configurationBuilder.Add(memConfigSrc3); var config = configurationBuilder.Build(); var dict = config.AsEnumerable(makePathsRelative: removePath).ToDictionary(k => k.Key, v => v.Value); // Assert Assert.Equal("Value1", dict["Mem1"]); Assert.Equal("NoKeyValue1", dict["Mem1:"]); Assert.Equal("ValueDeep1", dict["Mem1:KeyInMem1:Deep1"]); Assert.Equal("ValueInMem2", dict["Mem2:KeyInMem2"]); Assert.Equal("Value2", dict["Mem2"]); Assert.Equal("NoKeyValue2", dict["Mem2:"]); Assert.Equal("ValueDeep2", dict["Mem2:KeyInMem2:Deep2"]); Assert.Equal("Value3", dict["Mem3"]); Assert.Equal("NoKeyValue3", dict["Mem3:"]); Assert.Equal("ValueInMem3", dict["Mem3:KeyInMem3"]); Assert.Equal("ValueDeep3", dict["Mem3:KeyInMem3:Deep3"]); } [Fact] public void AsEnumerateStripsKeyFromChildren() { // Arrange var dic1 = new Dictionary() { {"Mem1", "Value1"}, {"Mem1:", "NoKeyValue1"}, {"Mem1:KeyInMem1", "ValueInMem1"}, {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} }; var dic2 = new Dictionary() { {"Mem2", "Value2"}, {"Mem2:", "NoKeyValue2"}, {"Mem2:KeyInMem2", "ValueInMem2"}, {"Mem2:KeyInMem2:Deep2", "ValueDeep2"} }; var dic3 = new Dictionary() { {"Mem3", "Value3"}, {"Mem3:", "NoKeyValue3"}, {"Mem3:KeyInMem3", "ValueInMem3"}, {"Mem3:KeyInMem4", "ValueInMem4"}, {"Mem3:KeyInMem3:Deep3", "ValueDeep3"}, {"Mem3:KeyInMem3:Deep4", "ValueDeep4"} }; var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; var configurationBuilder = new ConfigurationBuilder(); // Act configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); configurationBuilder.Add(memConfigSrc3); var config = configurationBuilder.Build(); var dict = config.GetSection("Mem1").AsEnumerable(makePathsRelative: true).ToDictionary(k => k.Key, v => v.Value); Assert.Equal(3, dict.Count); Assert.Equal("NoKeyValue1", dict[""]); Assert.Equal("ValueInMem1", dict["KeyInMem1"]); Assert.Equal("ValueDeep1", dict["KeyInMem1:Deep1"]); var dict2 = config.GetSection("Mem2").AsEnumerable(makePathsRelative: true).ToDictionary(k => k.Key, v => v.Value); Assert.Equal(3, dict2.Count); Assert.Equal("NoKeyValue2", dict2[""]); Assert.Equal("ValueInMem2", dict2["KeyInMem2"]); Assert.Equal("ValueDeep2", dict2["KeyInMem2:Deep2"]); var dict3 = config.GetSection("Mem3").AsEnumerable(makePathsRelative: true).ToDictionary(k => k.Key, v => v.Value); Assert.Equal(5, dict3.Count); Assert.Equal("NoKeyValue3", dict3[""]); Assert.Equal("ValueInMem3", dict3["KeyInMem3"]); Assert.Equal("ValueInMem4", dict3["KeyInMem4"]); Assert.Equal("ValueDeep3", dict3["KeyInMem3:Deep3"]); Assert.Equal("ValueDeep4", dict3["KeyInMem3:Deep4"]); } [Fact] public void NewConfigurationProviderOverridesOldOneWhenKeyIsDuplicated() { // Arrange var dic1 = new Dictionary() { {"Key1:Key2", "ValueInMem1"} }; var dic2 = new Dictionary() { {"Key1:Key2", "ValueInMem2"} }; var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var configurationBuilder = new ConfigurationBuilder(); // Act configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); var config = configurationBuilder.Build(); // Assert Assert.Equal("ValueInMem2", config["Key1:Key2"]); } public class TestMemorySourceProvider : MemoryConfigurationProvider, IConfigurationSource { public TestMemorySourceProvider(Dictionary initialData) : base(new MemoryConfigurationSource { InitialData = initialData }) { } public IConfigurationProvider Build(IConfigurationBuilder builder) { return this; } } [Fact] public void SettingValueUpdatesAllConfigurationProviders() { // Arrange var dict = new Dictionary() { {"Key1", "Value1"}, {"Key2", "Value2"} }; var memConfigSrc1 = new TestMemorySourceProvider(dict); var memConfigSrc2 = new TestMemorySourceProvider(dict); var memConfigSrc3 = new TestMemorySourceProvider(dict); var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); configurationBuilder.Add(memConfigSrc3); var config = configurationBuilder.Build(); // Act config["Key1"] = "NewValue1"; config["Key2"] = "NewValue2"; var memConfigProvider1 = memConfigSrc1.Build(configurationBuilder); var memConfigProvider2 = memConfigSrc2.Build(configurationBuilder); var memConfigProvider3 = memConfigSrc3.Build(configurationBuilder); // Assert Assert.Equal("NewValue1", config["Key1"]); Assert.Equal("NewValue1", memConfigProvider1.Get("Key1")); Assert.Equal("NewValue1", memConfigProvider2.Get("Key1")); Assert.Equal("NewValue1", memConfigProvider3.Get("Key1")); Assert.Equal("NewValue2", config["Key2"]); Assert.Equal("NewValue2", memConfigProvider1.Get("Key2")); Assert.Equal("NewValue2", memConfigProvider2.Get("Key2")); Assert.Equal("NewValue2", memConfigProvider3.Get("Key2")); } [Fact] public void CanGetConfigurationSection() { // Arrange var dic1 = new Dictionary() { {"Data:DB1:Connection1", "MemVal1"}, {"Data:DB1:Connection2", "MemVal2"} }; var dic2 = new Dictionary() { {"DataSource:DB2:Connection", "MemVal3"} }; var dic3 = new Dictionary() { {"Data", "MemVal4"} }; var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); configurationBuilder.Add(memConfigSrc3); var config = configurationBuilder.Build(); // Act var configFocus = config.GetSection("Data"); var memVal1 = configFocus["DB1:Connection1"]; var memVal2 = configFocus["DB1:Connection2"]; var memVal3 = configFocus["DB2:Connection"]; var memVal4 = configFocus["Source:DB2:Connection"]; var memVal5 = configFocus.Value; // Assert Assert.Equal("MemVal1", memVal1); Assert.Equal("MemVal2", memVal2); Assert.Equal("MemVal4", memVal5); Assert.Equal("MemVal1", configFocus["DB1:Connection1"]); Assert.Equal("MemVal2", configFocus["DB1:Connection2"]); Assert.Null(configFocus["DB2:Connection"]); Assert.Null(configFocus["Source:DB2:Connection"]); Assert.Equal("MemVal4", configFocus.Value); } [Fact] public void CanGetConnectionStrings() { // Arrange var dic1 = new Dictionary() { {"ConnectionStrings:DB1:Connection1", "MemVal1"}, {"ConnectionStrings:DB1:Connection2", "MemVal2"} }; var dic2 = new Dictionary() { {"ConnectionStrings:DB2:Connection", "MemVal3"} }; var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); var config = configurationBuilder.Build(); // Act var memVal1 = config.GetConnectionString("DB1:Connection1"); var memVal2 = config.GetConnectionString("DB1:Connection2"); var memVal3 = config.GetConnectionString("DB2:Connection"); // Assert Assert.Equal("MemVal1", memVal1); Assert.Equal("MemVal2", memVal2); Assert.Equal("MemVal3", memVal3); } [Fact] public void CanGetConfigurationChildren() { // Arrange var dic1 = new Dictionary() { {"Data:DB1:Connection1", "MemVal1"}, {"Data:DB1:Connection2", "MemVal2"} }; var dic2 = new Dictionary() { {"Data:DB2Connection", "MemVal3"} }; var dic3 = new Dictionary() { {"DataSource:DB3:Connection", "MemVal4"} }; var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dic1 }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dic2 }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dic3 }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); configurationBuilder.Add(memConfigSrc3); var config = configurationBuilder.Build(); // Act var configSections = config.GetSection("Data").GetChildren().ToList(); // Assert Assert.Equal(2, configSections.Count()); Assert.Equal("MemVal1", configSections.FirstOrDefault(c => c.Key == "DB1")["Connection1"]); Assert.Equal("MemVal2", configSections.FirstOrDefault(c => c.Key == "DB1")["Connection2"]); Assert.Equal("MemVal3", configSections.FirstOrDefault(c => c.Key == "DB2Connection").Value); Assert.False(configSections.Exists(c => c.Key == "DB3")); Assert.False(configSections.Exists(c => c.Key == "DB3")); } [Fact] public void SourcesReturnsAddedConfigurationProviders() { // Arrange var dict = new Dictionary() { {"Mem:KeyInMem", "MemVal"} }; var memConfigSrc1 = new MemoryConfigurationSource { InitialData = dict }; var memConfigSrc2 = new MemoryConfigurationSource { InitialData = dict }; var memConfigSrc3 = new MemoryConfigurationSource { InitialData = dict }; var srcSet = new HashSet() { memConfigSrc1, memConfigSrc2, memConfigSrc3 }; var configurationBuilder = new ConfigurationBuilder(); // Act configurationBuilder.Add(memConfigSrc1); configurationBuilder.Add(memConfigSrc2); configurationBuilder.Add(memConfigSrc3); var config = configurationBuilder.Build(); // Assert Assert.Equal(new[] { memConfigSrc1, memConfigSrc2, memConfigSrc3 }, configurationBuilder.Sources); } [Fact] public void SetValueThrowsExceptionNoSourceRegistered() { // Arrange var configurationBuilder = new ConfigurationBuilder(); var config = configurationBuilder.Build(); var expectedMsg = Resources.Error_NoSources; // Act var ex = Assert.Throws(() => config["Title"] = "Welcome"); // Assert Assert.Equal(expectedMsg, ex.Message); } [Fact] public void SameReloadTokenIsReturnedRepeatedly() { // Arrange var configurationBuilder = new ConfigurationBuilder(); var config = configurationBuilder.Build(); // Act var token1 = config.GetReloadToken(); var token2 = config.GetReloadToken(); // Assert Assert.Same(token1, token2); } [Fact] public void DifferentReloadTokenReturnedAfterReloading() { // Arrange var configurationBuilder = new ConfigurationBuilder(); var config = configurationBuilder.Build(); // Act var token1 = config.GetReloadToken(); var token2 = config.GetReloadToken(); config.Reload(); var token3 = config.GetReloadToken(); var token4 = config.GetReloadToken(); // Assert Assert.Same(token1, token2); Assert.Same(token3, token4); Assert.NotSame(token1, token3); } [Fact] public void TokenTriggeredWhenReloadOccurs() { // Arrange var configurationBuilder = new ConfigurationBuilder(); var config = configurationBuilder.Build(); // Act var token1 = config.GetReloadToken(); var hasChanged1 = token1.HasChanged; config.Reload(); var hasChanged2 = token1.HasChanged; // Assert Assert.False(hasChanged1); Assert.True(hasChanged2); } [Fact] public void MultipleCallbacksCanBeRegisteredToReload() { // Arrange var configurationBuilder = new ConfigurationBuilder(); var config = configurationBuilder.Build(); // Act var token1 = config.GetReloadToken(); var called1 = 0; token1.RegisterChangeCallback(_ => called1++, state: null); var called2 = 0; token1.RegisterChangeCallback(_ => called2++, state: null); // Assert Assert.Equal(0, called1); Assert.Equal(0, called2); config.Reload(); Assert.Equal(1, called1); Assert.Equal(1, called2); var token2 = config.GetReloadToken(); var cleanup1 = token2.RegisterChangeCallback(_ => called1++, state: null); token2.RegisterChangeCallback(_ => called2++, state: null); cleanup1.Dispose(); config.Reload(); Assert.Equal(1, called1); Assert.Equal(2, called2); } [Fact] public void NewTokenAfterReloadIsNotChanged() { // Arrange var configurationBuilder = new ConfigurationBuilder(); var config = configurationBuilder.Build(); // Act var token1 = config.GetReloadToken(); var hasChanged1 = token1.HasChanged; config.Reload(); var hasChanged2 = token1.HasChanged; var token2 = config.GetReloadToken(); var hasChanged3 = token2.HasChanged; // Assert Assert.False(hasChanged1); Assert.True(hasChanged2); Assert.False(hasChanged3); Assert.NotSame(token1, token2); } [Fact] public void KeyStartingWithColonMeansFirstSectionHasEmptyName() { // Arrange var dict = new Dictionary { [":Key2"] = "value" }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dict); var config = configurationBuilder.Build(); // Act var children = config.GetChildren().ToArray(); // Assert Assert.Single(children); Assert.Equal(string.Empty, children.First().Key); Assert.Single(children.First().GetChildren()); Assert.Equal("Key2", children.First().GetChildren().First().Key); } [Fact] public void KeyWithDoubleColonHasSectionWithEmptyName() { // Arrange var dict = new Dictionary { ["Key1::Key3"] = "value" }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dict); var config = configurationBuilder.Build(); // Act var children = config.GetChildren().ToArray(); // Assert Assert.Single(children); Assert.Equal("Key1", children.First().Key); Assert.Single(children.First().GetChildren()); Assert.Equal(string.Empty, children.First().GetChildren().First().Key); Assert.Single(children.First().GetChildren().First().GetChildren()); Assert.Equal("Key3", children.First().GetChildren().First().GetChildren().First().Key); } [Fact] public void KeyEndingWithColonMeansLastSectionHasEmptyName() { // Arrange var dict = new Dictionary { ["Key1:"] = "value" }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dict); var config = configurationBuilder.Build(); // Act var children = config.GetChildren().ToArray(); // Assert Assert.Single(children); Assert.Equal("Key1", children.First().Key); Assert.Single(children.First().GetChildren()); Assert.Equal(string.Empty, children.First().GetChildren().First().Key); } [Fact] public void SectionWithValueExists() { // Arrange var dict = new Dictionary() { {"Mem1", "Value1"}, {"Mem1:KeyInMem1", "ValueInMem1"}, {"Mem1:KeyInMem1:Deep1", "ValueDeep1"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dict); var config = configurationBuilder.Build(); // Act var sectionExists1 = config.GetSection("Mem1").Exists(); var sectionExists2 = config.GetSection("Mem1:KeyInMem1").Exists(); var sectionNotExists = config.GetSection("Mem2").Exists(); // Assert Assert.True(sectionExists1); Assert.True(sectionExists2); Assert.False(sectionNotExists); } [Fact] public void SectionWithChildrenExists() { // Arrange var dict = new Dictionary() { {"Mem1:KeyInMem1", "ValueInMem1"}, {"Mem1:KeyInMem1:Deep1", "ValueDeep1"}, {"Mem2:KeyInMem2:Deep1", "ValueDeep2"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dict); var config = configurationBuilder.Build(); // Act var sectionExists1 = config.GetSection("Mem1").Exists(); var sectionExists2 = config.GetSection("Mem2").Exists(); var sectionNotExists = config.GetSection("Mem3").Exists(); // Assert Assert.True(sectionExists1); Assert.True(sectionExists2); Assert.False(sectionNotExists); } [Fact] public void NullSectionDoesNotExist() { // Arrange // Act var sectionExists = ConfigurationExtensions.Exists(null); // Assert Assert.False(sectionExists); } } } ================================================ FILE: test/Config.Test.Common/Config.Test.Common.csproj ================================================  Microsoft.Extensions.Configuration.Test.Common Microsoft.Extensions.Configuration.Test.Common netstandard2.0 ================================================ FILE: test/Config.Test.Common/ConfigurationProviderExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Extensions.Configuration.Test { public static class ConfigurationProviderExtensions { public static string Get(this IConfigurationProvider provider, string key) { string value; if (!provider.TryGet(key, out value)) { throw new InvalidOperationException("Key not found"); } return value; } } } ================================================ FILE: test/Config.Test.Common/TestStreamHelpers.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; namespace Microsoft.Extensions.Configuration.Test { public static class TestStreamHelpers { public static readonly string ArbitraryFilePath = "Unit tests do not touch file system"; public static IFileProvider StringToFileProvider(string str) { return new TestFileProvider(str); } private class TestFile : IFileInfo { private readonly string _data; public TestFile(string str) { _data = str; } public bool Exists { get { return true; } } public bool IsDirectory { get { return false; } } public DateTimeOffset LastModified { get { throw new NotImplementedException(); } } public long Length { get { throw new NotImplementedException(); } } public string Name { get { throw new NotImplementedException(); } } public string PhysicalPath { get { throw new NotImplementedException(); } } public Stream CreateReadStream() { return StringToStream(_data); } } private class TestFileProvider : IFileProvider { private string _data; public TestFileProvider(string str) { _data = str; } public IDirectoryContents GetDirectoryContents(string subpath) { throw new NotImplementedException(); } public IFileInfo GetFileInfo(string subpath) { return new TestFile(_data); } public IChangeToken Watch(string filter) { throw new NotImplementedException(); } } public static Stream StringToStream(string str) { var memStream = new MemoryStream(); var textWriter = new StreamWriter(memStream); textWriter.Write(str); textWriter.Flush(); memStream.Seek(0, SeekOrigin.Begin); return memStream; } public static string StreamToString(Stream stream) { stream.Seek(0, SeekOrigin.Begin); var reader = new StreamReader(stream); return reader.ReadToEnd(); } } } ================================================ FILE: test/Config.UserSecrets.Test/Config.UserSecrets.Test.csproj ================================================ Microsoft.Extensions.Configuration.UserSecrets.Test Microsoft.Extensions.Configuration.UserSecrets.Test netcoreapp2.2 <_Parameter1>TargetFramework <_Parameter2>$(TargetFramework) ================================================ FILE: test/Config.UserSecrets.Test/ConfigurationExtensionTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Reflection; using System.Text; using Microsoft.Extensions.Configuration.UserSecrets; using Microsoft.Extensions.Configuration.UserSecrets.Test; using Newtonsoft.Json.Linq; using Xunit; [assembly: UserSecretsId(ConfigurationExtensionTest.TestSecretsId)] namespace Microsoft.Extensions.Configuration.UserSecrets.Test { public class ConfigurationExtensionTest : IDisposable { public const string TestSecretsId = "d6076a6d3ab24c00b2511f10a56c68cc"; private List _tmpDirectories = new List(); private void SetSecret(string id, string key, string value) { var secretsFilePath = PathHelper.GetSecretsPathFromSecretsId(id); var dir = Path.GetDirectoryName(secretsFilePath); Directory.CreateDirectory(dir); _tmpDirectories.Add(dir); var secrets = new ConfigurationBuilder() .AddJsonFile(secretsFilePath, optional: true) .Build() .AsEnumerable() .Where(i => i.Value != null) .ToDictionary(i => i.Key, i => i.Value, StringComparer.OrdinalIgnoreCase); secrets[key] = value; var contents = new JObject(); if (secrets != null) { foreach (var secret in secrets.AsEnumerable()) { contents[secret.Key] = secret.Value; } } File.WriteAllText(secretsFilePath, contents.ToString(), Encoding.UTF8); } [Fact] public void AddUserSecrets_FindsAssemblyAttribute() { var randValue = Guid.NewGuid().ToString(); var configKey = "MyDummySetting"; SetSecret(TestSecretsId, configKey, randValue); var config = new ConfigurationBuilder() .AddUserSecrets(typeof(ConfigurationExtensionTest).GetTypeInfo().Assembly) .Build(); Assert.Equal(randValue, config[configKey]); } [Fact] public void AddUserSecrets_FindsAssemblyAttributeFromType() { var randValue = Guid.NewGuid().ToString(); var configKey = "MyDummySetting"; SetSecret(TestSecretsId, configKey, randValue); var config = new ConfigurationBuilder() .AddUserSecrets() .Build(); Assert.Equal(randValue, config[configKey]); } [Fact] public void AddUserSecrets_ThrowsIfAssemblyAttributeFromType() { var ex = Assert.Throws(() => new ConfigurationBuilder().AddUserSecrets()); Assert.Equal(Resources.FormatError_Missing_UserSecretsIdAttribute(typeof(string).GetTypeInfo().Assembly.GetName().Name), ex.Message); ex = Assert.Throws(() => new ConfigurationBuilder().AddUserSecrets(typeof(JObject).Assembly)); Assert.Equal(Resources.FormatError_Missing_UserSecretsIdAttribute(typeof(JObject).GetTypeInfo().Assembly.GetName().Name), ex.Message); } [Fact] public void AddUserSecrets_DoesNotThrowsIfOptional() { var config = new ConfigurationBuilder() .AddUserSecrets(optional: true) .AddUserSecrets(typeof(List<>).Assembly, optional: true) .Build(); Assert.Empty(config.AsEnumerable()); } [Fact] public void AddUserSecrets_With_SecretsId_Passed_Explicitly() { var userSecretsId = Guid.NewGuid().ToString(); SetSecret(userSecretsId, "Facebook:AppSecret", "value1"); var builder = new ConfigurationBuilder().AddUserSecrets(userSecretsId); var configuration = builder.Build(); Assert.Equal("value1", configuration["Facebook:AppSecret"]); } [Fact] public void AddUserSecrets_Does_Not_Fail_On_Non_Existing_File() { var userSecretsId = Guid.NewGuid().ToString(); var secretFilePath = PathHelper.GetSecretsPathFromSecretsId(userSecretsId); var builder = new ConfigurationBuilder().AddUserSecrets(userSecretsId); var configuration = builder.Build(); Assert.Null(configuration["Facebook:AppSecret"]); Assert.False(File.Exists(secretFilePath)); } public void Dispose() { foreach (var dir in _tmpDirectories) { try { if (Directory.Exists(dir)) { Directory.Delete(dir, true); } } catch { Console.WriteLine("Failed to delete " + dir); } } } } } ================================================ FILE: test/Config.UserSecrets.Test/MsBuildTargetTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using Xunit; using Xunit.Abstractions; namespace Microsoft.Extensions.Configuration.UserSecrets { public class MsBuildTargetTest : IDisposable { private readonly string _tempDir; private readonly DirectoryInfo _solutionRoot; private readonly ITestOutputHelper _output; public MsBuildTargetTest(ITestOutputHelper output) { _output = output; _tempDir = Path.Combine(AppContext.BaseDirectory, Path.GetRandomFileName()); Directory.CreateDirectory(_tempDir); _solutionRoot = new DirectoryInfo(AppContext.BaseDirectory); while (_solutionRoot != null) { if (File.Exists(Path.Combine(_solutionRoot.FullName, "NuGet.config"))) { break; } _solutionRoot = _solutionRoot.Parent; } if (_solutionRoot == null) { throw new FileNotFoundException("Could not identify solution root"); } } [Theory] [InlineData(".csproj", ".cs")] [InlineData(".fsproj", ".fs")] public void GeneratesAssemblyAttributeFile(string projectExt, string sourceExt) { var testTfm = typeof(MsBuildTargetTest).Assembly .GetCustomAttributes() .First(f => f.Key == "TargetFramework") .Value; var target = Path.Combine(_solutionRoot.FullName, "src", "Config.UserSecrets", "build", "netstandard2.0", "Microsoft.Extensions.Configuration.UserSecrets.targets"); Directory.CreateDirectory(Path.Combine(_tempDir, "obj")); var libName = "Microsoft.Extensions.Configuration.UserSecrets.dll"; File.Copy(Path.Combine(AppContext.BaseDirectory, libName), Path.Combine(_tempDir, libName)); File.Copy(target, Path.Combine(_tempDir, "obj", $"test{projectExt}.usersecretstest.targets")); // imitates how NuGet will import this target var testProj = Path.Combine(_tempDir, "test" + projectExt); // should represent a 'dotnet new' project File.WriteAllText(testProj, $@" 1.0.0 Exe xyz123 {testTfm} false "); _output.WriteLine($"Tempdir = {_tempDir}"); switch (projectExt) { case ".csproj": File.WriteAllText(Path.Combine(_tempDir, "Program.cs"), "public class Program { public static void Main(){}}"); break; case ".fsproj": File.WriteAllText(Path.Combine(_tempDir, "Program.fs"), @" module SomeNamespace.SubNamespace open System [] let main argv = printfn ""Hello World from F#!"" 0 "); break; } var assemblyInfoFile = Path.Combine(_tempDir, $"obj/Debug/{testTfm}/test.AssemblyInfo" + sourceExt); AssertDotNet("restore"); Assert.False(File.Exists(assemblyInfoFile), $"{assemblyInfoFile} should not exist but does"); AssertDotNet("build --configuration Debug"); Assert.True(File.Exists(assemblyInfoFile), $"{assemblyInfoFile} should not exist but does not"); var contents = File.ReadAllText(assemblyInfoFile); Assert.Contains("assembly: Microsoft.Extensions.Configuration.UserSecrets.UserSecretsIdAttribute(\"xyz123\")", contents); var lastWrite = new FileInfo(assemblyInfoFile).LastWriteTimeUtc; AssertDotNet("build --configuration Debug"); // asserts that the target doesn't re-generate assembly file. Important for incremental build. Assert.Equal(lastWrite, new FileInfo(assemblyInfoFile).LastWriteTimeUtc); } private void AssertDotNet(string args) { void LogData(object obj, DataReceivedEventArgs e) { _output.WriteLine(e.Data ?? string.Empty); } var processInfo = new ProcessStartInfo { FileName = "dotnet", Arguments = args, UseShellExecute = false, WorkingDirectory = _tempDir, RedirectStandardOutput = true, }; var process = new Process() { EnableRaisingEvents = true, StartInfo = processInfo }; process.OutputDataReceived += LogData; process.Start(); process.BeginOutputReadLine(); process.WaitForExit(); process.OutputDataReceived -= LogData; Assert.Equal(0, process.ExitCode); } public void Dispose() { try { Directory.Delete(_tempDir, recursive: true); } catch { Console.Error.WriteLine($"Failed to delete '{_tempDir}' during test cleanup"); } } } } ================================================ FILE: test/Config.UserSecrets.Test/PathHelperTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using System.Linq; using Xunit; namespace Microsoft.Extensions.Configuration.UserSecrets.Test { public class PathHelperTest { [Fact] public void Gives_Correct_Secret_Path() { var userSecretsId = "abcxyz123"; var actualSecretPath = PathHelper.GetSecretsPathFromSecretsId(userSecretsId); var root = Environment.GetEnvironmentVariable("APPDATA") ?? // On Windows it goes to %APPDATA%\Microsoft\UserSecrets\ Environment.GetEnvironmentVariable("HOME"); // On Mac/Linux it goes to ~/.microsoft/usersecrets/ var expectedSecretPath = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPDATA")) ? Path.Combine(root, "Microsoft", "UserSecrets", userSecretsId, "secrets.json") : Path.Combine(root, ".microsoft", "usersecrets", userSecretsId, "secrets.json"); Assert.Equal(expectedSecretPath, actualSecretPath); } [Fact] public void Throws_If_UserSecretId_Contains_Invalid_Characters() { foreach (var character in Path.GetInvalidPathChars().Concat(Path.GetInvalidFileNameChars())) { var id = "Test" + character; Assert.Throws(() => PathHelper.GetSecretsPathFromSecretsId(id)); } } } } ================================================ FILE: test/Config.Xml.Test/Config.Xml.Test.csproj ================================================  Microsoft.Extensions.Configuration.Xml.Test Microsoft.Extensions.Configuration.Xml.Test $(StandardTestTfms) ================================================ FILE: test/Config.Xml.Test/XmlConfigurationExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using Xunit; namespace Microsoft.Extensions.Configuration.Xml.Test { public class XmlConfigurationExtensionsTest { [Fact] public void AddXmlFile_ThrowsIfFileDoesNotExistAtPath() { var config = new ConfigurationBuilder().AddXmlFile("NotExistingConfig.xml"); // Arrange // Act and Assert var ex = Assert.Throws(() => config.Build()); Assert.StartsWith($"The configuration file 'NotExistingConfig.xml' was not found and is not optional. The physical path is '", ex.Message); } } } ================================================ FILE: test/Config.Xml.Test/XmlConfigurationTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.Xml; using System.Xml; using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Configuration.Test; using Xunit; namespace Microsoft.Extensions.Configuration.Xml.Test { public class XmlConfigurationTest { [Fact] public void LoadKeyValuePairsFromValidXml() { var xml = @" Test.Connection.String SqlClient AnotherTestConnectionString MySql "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("Test.Connection.String", xmlConfigSrc.Get("DATA.SETTING:DEFAULTCONNECTION:CONNECTION.STRING")); Assert.Equal("SqlClient", xmlConfigSrc.Get("DATA.SETTING:DefaultConnection:Provider")); Assert.Equal("AnotherTestConnectionString", xmlConfigSrc.Get("data.setting:inventory:connectionstring")); Assert.Equal("MySql", xmlConfigSrc.Get("Data.setting:Inventory:Provider")); } [Fact] public void LoadMethodCanHandleEmptyValue() { var xml = @" "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal(string.Empty, xmlConfigSrc.Get("Key1")); Assert.Equal(string.Empty, xmlConfigSrc.Get("Key2:Key3")); } [Fact] public void CommonAttributesContributeToKeyValuePairs() { var xml = @" "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("8008", xmlConfigSrc.Get("Port")); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); Assert.Equal("AnotherTestConnectionString", xmlConfigSrc.Get("Data:Inventory:ConnectionString")); Assert.Equal("MySql", xmlConfigSrc.Get("Data:Inventory:Provider")); } [Fact] public void SupportMixingChildElementsAndAttributes() { var xml = @" TestConnectionString MySql "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("8008", xmlConfigSrc.Get("Port")); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); Assert.Equal("AnotherTestConnectionString", xmlConfigSrc.Get("Data:Inventory:ConnectionString")); Assert.Equal("MySql", xmlConfigSrc.Get("Data:Inventory:Provider")); } [Fact] public void NameAttributeContributesToPrefix() { var xml = @" TestConnectionString SqlClient AnotherTestConnectionString MySql "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("DefaultConnection", xmlConfigSrc.Get("Data:DefaultConnection:Name")); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); Assert.Equal("Inventory", xmlConfigSrc.Get("Data:Inventory:Name")); Assert.Equal("AnotherTestConnectionString", xmlConfigSrc.Get("Data:Inventory:ConnectionString")); Assert.Equal("MySql", xmlConfigSrc.Get("Data:Inventory:Provider")); } [Fact] public void NameAttributeInRootElementContributesToPrefix() { var xml = @" TestConnectionString SqlClient AnotherTestConnectionString MySql "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("Data", xmlConfigSrc.Get("Data:Name")); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); Assert.Equal("AnotherTestConnectionString", xmlConfigSrc.Get("Data:Inventory:ConnectionString")); Assert.Equal("MySql", xmlConfigSrc.Get("Data:Inventory:Provider")); } [Fact] public void SupportMixingNameAttributesAndCommonAttributes() { var xml = @" MySql "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("DefaultConnection", xmlConfigSrc.Get("Data:DefaultConnection:Name")); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); Assert.Equal("Inventory", xmlConfigSrc.Get("Data:Inventory:Name")); Assert.Equal("AnotherTestConnectionString", xmlConfigSrc.Get("Data:Inventory:ConnectionString")); Assert.Equal("MySql", xmlConfigSrc.Get("Data:Inventory:Provider")); } [Fact] public void SupportCDATAAsTextNode() { var xml = @" ]]> "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("SpecialStringWith<>", xmlConfigSrc.Get("Data:Inventory:Provider")); } [Fact] public void SupportAndIgnoreComments() { var xml = @" TestConnectionString SqlClient AnotherTestConnectionString MySql "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); Assert.Equal("AnotherTestConnectionString", xmlConfigSrc.Get("Data:Inventory:ConnectionString")); Assert.Equal("MySql", xmlConfigSrc.Get("Data:Inventory:Provider")); } [Fact] public void SupportAndIgnoreXMLDeclaration() { var xml = @" TestConnectionString SqlClient AnotherTestConnectionString MySql "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); Assert.Equal("AnotherTestConnectionString", xmlConfigSrc.Get("Data:Inventory:ConnectionString")); Assert.Equal("MySql", xmlConfigSrc.Get("Data:Inventory:Provider")); } [Fact] public void SupportAndIgnoreProcessingInstructions() { var xml = @" TestConnectionString SqlClient AnotherTestConnectionString MySql "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml)); Assert.Equal("TestConnectionString", xmlConfigSrc.Get("Data:DefaultConnection:ConnectionString")); Assert.Equal("SqlClient", xmlConfigSrc.Get("Data:DefaultConnection:Provider")); Assert.Equal("AnotherTestConnectionString", xmlConfigSrc.Get("Data:Inventory:ConnectionString")); Assert.Equal("MySql", xmlConfigSrc.Get("Data:Inventory:Provider")); } [Fact] [ReplaceCulture] public void ThrowExceptionWhenFindDTD() { var xml = @" ]> TestConnectionString SqlClient AnotherTestConnectionString MySql "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); var isMono = Type.GetType("Mono.Runtime") != null; var expectedMsg = isMono ? "Document Type Declaration (DTD) is prohibited in this XML. Line 1, position 10." : "For security reasons DTD is prohibited in this XML document. " + "To enable DTD processing set the DtdProcessing property on XmlReaderSettings " + "to Parse and pass the settings into XmlReader.Create method."; var exception = Assert.Throws(() => xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml))); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void ThrowExceptionWhenFindNamespace() { var xml = @" TestConnectionString SqlClient AnotherTestConnectionString MySql "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); var expectedMsg = Resources.FormatError_NamespaceIsNotSupported(Resources.FormatMsg_LineInfo(1, 11)); var exception = Assert.Throws(() => xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml))); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void ThrowExceptionWhenPassingNullAsFilePath() { var expectedMsg = new ArgumentException(Resources.Error_InvalidFilePath, "path").Message; var exception = Assert.Throws(() => new ConfigurationBuilder().AddXmlFile(path: null)); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void ThrowExceptionWhenPassingEmptyStringAsFilePath() { var expectedMsg = new ArgumentException(Resources.Error_InvalidFilePath, "path").Message; var exception = Assert.Throws(() => new ConfigurationBuilder().AddXmlFile(string.Empty)); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void ThrowExceptionWhenKeyIsDuplicated() { var xml = @" TestConnectionString SqlClient NewProvider "; var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); var expectedMsg = Resources.FormatError_KeyIsDuplicated("Data:DefaultConnection:ConnectionString", Resources.FormatMsg_LineInfo(8, 52)); var exception = Assert.Throws(() => xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xml))); Assert.Equal(expectedMsg, exception.Message); } [Fact] public void XmlConfiguration_Throws_On_Missing_Configuration_File() { var ex = Assert.Throws(() => new ConfigurationBuilder().AddXmlFile("NotExistingConfig.xml", optional: false).Build()); Assert.StartsWith($"The configuration file 'NotExistingConfig.xml' was not found and is not optional. The physical path is '", ex.Message); } [Fact] public void XmlConfiguration_Does_Not_Throw_On_Optional_Configuration() { var config = new ConfigurationBuilder().AddXmlFile("NotExistingConfig.xml", optional: true).Build(); } [ConditionalFact] [FrameworkSkipCondition(RuntimeFrameworks.Mono)] public void LoadKeyValuePairsFromValidEncryptedXml() { var xml = @" Test.Connection.String SqlClient AnotherTestConnectionString MySql "; // This AES key will be used to encrypt the 'Inventory' element var aes = Aes.Create(); aes.KeySize = 128; aes.GenerateKey(); // Perform the encryption var xmlDocument = new XmlDocument(); xmlDocument.LoadXml(xml); var encryptedXml = new EncryptedXml(xmlDocument); encryptedXml.AddKeyNameMapping("myKey", aes); var elementToEncrypt = (XmlElement)xmlDocument.SelectSingleNode("//Inventory"); EncryptedXml.ReplaceElement(elementToEncrypt, encryptedXml.Encrypt(elementToEncrypt, "myKey"), content: false); // Quick sanity check: the document should no longer contain an 'Inventory' element Assert.Null(xmlDocument.SelectSingleNode("//Inventory")); // Arrange var xmlConfigSrc = new XmlConfigurationProvider(new XmlConfigurationSource()); xmlConfigSrc.Decryptor = new XmlDocumentDecryptor(doc => { var innerEncryptedXml = new EncryptedXml(doc); innerEncryptedXml.AddKeyNameMapping("myKey", aes); return innerEncryptedXml; }); // Act xmlConfigSrc.Load(TestStreamHelpers.StringToStream(xmlDocument.OuterXml)); // Assert Assert.Equal("Test.Connection.String", xmlConfigSrc.Get("DATA.SETTING:DEFAULTCONNECTION:CONNECTION.STRING")); Assert.Equal("SqlClient", xmlConfigSrc.Get("DATA.SETTING:DefaultConnection:Provider")); Assert.Equal("AnotherTestConnectionString", xmlConfigSrc.Get("data.setting:inventory:connectionstring")); Assert.Equal("MySql", xmlConfigSrc.Get("Data.setting:Inventory:Provider")); } } } ================================================ FILE: test/Directory.Build.props ================================================ netcoreapp2.2 $(DeveloperBuildTestTfms) $(StandardTestTfms);net461 ================================================ FILE: version.props ================================================  3.0.0 alpha1 $(VersionPrefix) $(VersionPrefix)-$(VersionSuffix)-final t000 a- $(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-')) $(VersionSuffix)-$(BuildNumber)