diff --git a/OptionStudy.Next/5多样性配置源/ChainedConfigurationSourceTest.cs b/OptionStudy.Next/5多样性配置源/ChainedConfigurationSourceTest.cs
new file mode 100644
index 0000000..275d046
--- /dev/null
+++ b/OptionStudy.Next/5多样性配置源/ChainedConfigurationSourceTest.cs
@@ -0,0 +1,32 @@
+
+namespace OptionStudy.Next
+{
+ ///
+ /// Chained 配置源
+ ///
+ public class ChainedConfigurationSourceTest: IDisposable
+ {
+ private readonly ITestOutputHelper testOutput;
+
+ public ChainedConfigurationSourceTest(ITestOutputHelper testOutputHelper)
+ {
+ this.testOutput = testOutputHelper;
+ }
+
+ ///
+ /// 使用 Chained配置源
+ ///
+ [Fact]
+ public void Use_Test()
+ {
+
+
+ testOutput.WriteLine("使用 Chained 配置源!");
+ }
+
+ public void Dispose()
+ {
+
+ }
+ }
+}
diff --git a/OptionStudy.Next/5多样性配置源/CommandLineConfigurationSourceTest.cs b/OptionStudy.Next/5多样性配置源/CommandLineConfigurationSourceTest.cs
new file mode 100644
index 0000000..c109c40
--- /dev/null
+++ b/OptionStudy.Next/5多样性配置源/CommandLineConfigurationSourceTest.cs
@@ -0,0 +1,302 @@
+
+using OptionStudy.UnitApp.Next;
+
+namespace OptionStudy.Next
+{
+ ///
+ /// 命令行 配置源
+ /// 命令行参数分为
+ /// 单参数:用=将参数名和参数值按以下形式指定
+ /// {name}={value}
+ /// {prefix}{name}={value} prefix目前支持三种 “/”“--”“-”,其中“-”需配合 命令行参数映射一起使用
+ /// 双参数:使用 空格 将参数名和参数值隔开的形式,形如 {name}={value} 或 {prefix}{name}={value}
+ ///
+ public class CommandLineConfigurationSourceTest : IDisposable
+ {
+ private readonly ITestOutputHelper testOutput;
+
+ public CommandLineConfigurationSourceTest(ITestOutputHelper testOutputHelper)
+ {
+ this.testOutput = testOutputHelper;
+ }
+
+ ///
+ /// 创建 CommandLineConfigurationProvider
+ ///
+ [Fact]
+ public void Create_CommandLineConfigurationProvider_Test()
+ {
+ var args = new string[]
+ {
+ "AppName=argAppName",
+ "/AppVersion=1.1.1.1"
+ };
+ var cmdLineProvier = new CommandLineConfigurationProvider(args);
+ cmdLineProvier.Load();
+
+ var childKeys = cmdLineProvier.GetChildKeys(new string[0],null);
+ cmdLineProvier.TryGet("AppName", out string? appName);
+ cmdLineProvier.TryGet("AppVersion", out string? appVersion);
+
+ Assert.Equal(2,childKeys.Count());
+ Assert.Equal("argAppName", appName);
+ Assert.Equal("1.1.1.1", appVersion);
+ }
+
+ ///
+ /// 使用 CommandLineConfiguration
+ ///
+ [Fact]
+ public void Build_CommandLineConfiguration_Test()
+ {
+ var args = new string[]
+ {
+ "AppName=argAppName",
+ "/AppVersion=1.1.1.1",
+ "--EMail:ReceiveAddress=arg@163.com",
+ "--EMail:Recipient=arg",
+ };
+
+ var root = new ConfigurationBuilder().AddCommandLine(args).Build();
+
+ var option = root.Get();
+
+ Assert.NotNull(option);
+ Assert.Equal("argAppName",option.AppName);
+ Assert.Equal(new Version(1,1,1,1), option.AppVersion);
+ Assert.Equal("arg@163.com", option.EMail?.ReceiveAddress);
+ Assert.Equal("arg", option.EMail?.Recipient);
+ }
+
+ ///
+ /// 忽略中间不规范的值
+ ///
+ [Fact]
+ public void IgnoreValuesInMiddle_Test()
+ {
+ var args = new string[]
+ {
+ "Key1=Value1",
+ "--Key2=Value2",
+ "/Key3=Value3",
+ "Bogus1", //忽略,只有一个名没有值
+ "--Key4", "Value4",
+ "Bogus2", //忽略,只有一个名没有值
+ "/Key5", "Value5",
+ "Bogus3" //忽略,只有一个名没有值
+ };
+
+ var root = new ConfigurationBuilder().AddCommandLine(args).Build();
+
+ Assert.Equal("Value1", root.GetValue("Key1"));
+ Assert.Equal("Value2", root.GetValue("Key2"));
+ Assert.Equal("Value3", root.GetValue("Key3"));
+ Assert.Equal("Value4", root.GetValue("Key4"));
+ Assert.Equal("Value5", root.GetValue("Key5"));
+ Assert.Equal(5, root.GetChildren().Count());
+ }
+
+ ///
+ /// 使用参数映射:-前辍必需配合参数映射一起使用
+ ///
+ [Fact]
+ public void Use_SwitchMappings_Test()
+ {
+ 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 root = new ConfigurationBuilder().AddCommandLine(args,switchMappings).Build();
+
+ Assert.Equal("Value1", root.GetValue("LongKey1"));
+ Assert.Equal("Value2", root.GetValue("SuperLongKey2"));
+ Assert.Equal("Value3", root.GetValue("Key3"));
+ Assert.Equal("Value4", root.GetValue("Key4"));
+ Assert.Equal("Value5", root.GetValue("Key5"));
+ Assert.Equal("Value6", root.GetValue("SuchALongKey6"));
+ }
+
+ ///
+ /// 参数映射错误时,抛出异常
+ ///
+ [Fact]
+ public void ThrowException_SwitchMappings_HasError_Test()
+ {
+ // 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("switchMappings").Message;
+
+ // Act
+ var exception = Assert.Throws
+ (
+ () => new CommandLineConfigurationProvider(args, switchMappings)
+ );
+
+ // Assert
+ Assert.Contains(expectedMsg, exception.Message);
+ }
+
+ ///
+ /// 包含无效键名时,抛出异常
+ ///
+ [Fact]
+ public void ThrowException_SwitchMappings_ContainInvalidKey_Test()
+ {
+ 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" }
+ };
+
+ Assert.Throws
+ (
+ () => new CommandLineConfigurationProvider(args, switchMappings)
+ );
+ }
+
+ ///
+ /// 命令行参数为null,抛出异常
+ ///
+ [Fact]
+ public void ThrowException_NullIsPassedToConstructorAsArgs_Test()
+ {
+ 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_Test()
+ {
+ var args = new string[]
+ {
+ "/Key1=Value1",
+ "--Key1=Value2"
+ };
+ var cmdLineConfig = new CommandLineConfigurationProvider(args);
+
+ cmdLineConfig.Load();
+
+ cmdLineConfig.TryGet("Key1", out string? value1);
+
+ Assert.Equal("Value2", value1);
+ }
+
+ ///
+ /// 忽略缺少Key的值
+ ///
+ [Fact]
+ public void IgnoreWhenValueForAKeyIsMissing_Test()
+ {
+ var args = new string[]
+ {
+ "--Key1", "Value1",
+ "/Key2" /* The value for Key2 is missing here */
+ };
+
+ var cmdLineConfig = new CommandLineConfigurationProvider(args);
+ cmdLineConfig.Load();
+
+ cmdLineConfig.TryGet("Key1", out string? value1);
+
+ Assert.Single(cmdLineConfig.GetChildKeys(new string[0], null));
+ Assert.Equal("Value1", value1);
+ }
+
+ ///
+ /// 忽略无法识别的参数
+ ///
+ [Fact]
+ public void IgnoreWhenAnArgumentCannotBeRecognized_Test()
+ {
+ var args = new string[]
+ {
+ "ArgWithoutPrefixAndEqualSign"
+ };
+ var cmdLineConfig = new CommandLineConfigurationProvider(args);
+ cmdLineConfig.Load();
+ Assert.Empty(cmdLineConfig.GetChildKeys(new string[0], null));
+ }
+
+ ///
+ /// 忽略未被映射的参数
+ /// 如果没有映射,则异常;有映射,但key不在映射中时,则忽略
+ ///
+ [Fact]
+ public void IgnoreWhenShortSwitchNotDefined_Test()
+ {
+ 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], ""));
+ }
+
+ public void Dispose()
+ {
+
+ }
+ }
+}
diff --git a/OptionStudy.Next/5多样性配置源/EnvironmentVariablesConfigurationSourceTest.cs b/OptionStudy.Next/5多样性配置源/EnvironmentVariablesConfigurationSourceTest.cs
new file mode 100644
index 0000000..bded6c1
--- /dev/null
+++ b/OptionStudy.Next/5多样性配置源/EnvironmentVariablesConfigurationSourceTest.cs
@@ -0,0 +1,76 @@
+
+
+
+using OptionStudy.UnitApp.Next;
+
+namespace OptionStudy.Next
+{
+ ///
+ /// 环境变量 配置源
+ /// 环境变量分类:系统环境变量、用户环境变量、进程环境变量
+ /// 环境变量设置:系统图形工具、编程、启动文件 launchSettings.json(VS环境比较方便)
+ ///
+ public class EnvironmentVariablesConfigurationSourceTest : IDisposable
+ {
+ private readonly ITestOutputHelper testOutput;
+
+ public EnvironmentVariablesConfigurationSourceTest(ITestOutputHelper testOutputHelper)
+ {
+ this.testOutput = testOutputHelper;
+ }
+
+ ///
+ /// 设置的进程环境变量,在一次测试运行中会保留
+ /// 因为一次测试运行中的多个测试方法,是在一个进程中运行的。
+ ///
+ [Theory]
+ [InlineData("xUnit-First")]
+ [InlineData("xUnit-Second")]
+ public void ProcessEnvironmentVariables_Set_Test(string value)
+ {
+ var envName = "xUnit:AppName";
+ var env = Environment.GetEnvironmentVariable(envName);
+ //一次运行多个测试用例时:
+ //第一个测试用例运行时,是null值,但第二个用例运行时就有值,其值为第一个用例设置的值
+ if (env == null)
+ {
+ testOutput.WriteLine($"当前用例参数为 {value}, 环境变量 {nameof(envName)} 的值为 null ");
+ }
+ else
+ {
+ testOutput.WriteLine($"当前用例参数为 {value}, 环境变量 {nameof(envName)} 的值为 {env} ");
+ }
+
+ //设置环境变量,看看在另一个测试工里会不会保留设置的环境变量值
+ Environment.SetEnvironmentVariable(envName, value, EnvironmentVariableTarget.Process);
+ }
+
+ ///
+ /// 设置进程环境变量
+ ///
+ [Fact]
+ public void Load_Set_ProcessEnvironmentVariables_Test()
+ {
+ // 设置进程环境变量
+ Environment.SetEnvironmentVariable("xUnit:AppName", "xunitAppName", EnvironmentVariableTarget.Process);
+ Environment.SetEnvironmentVariable("xUnit:AppVersion", "2.2.2.2", EnvironmentVariableTarget.Process);
+ Environment.SetEnvironmentVariable("xUnit:EMail:ReceiveAddress", "xunit@163.com", EnvironmentVariableTarget.Process);
+ Environment.SetEnvironmentVariable("xUnit:Email:Recipient", "xunit", EnvironmentVariableTarget.Process);
+
+ // 获取配置对象
+ var root = new ConfigurationBuilder().AddEnvironmentVariables("xUnit:").Build();
+ var option = root.Get();
+
+ Assert.NotNull(option);
+ Assert.Equal("xunitAppName",option.AppName);
+ Assert.Equal(new Version(2,2,2,2),option.AppVersion);
+ Assert.Equal("xunit@163.com",option.EMail?.ReceiveAddress);
+ Assert.Equal("xunit",option.EMail?.Recipient);
+ }
+
+ public void Dispose()
+ {
+
+ }
+ }
+}
diff --git a/OptionStudy.Next/5多样性配置源/MemoryConfigurationSourceTest.cs b/OptionStudy.Next/5多样性配置源/MemoryConfigurationSourceTest.cs
new file mode 100644
index 0000000..91f6095
--- /dev/null
+++ b/OptionStudy.Next/5多样性配置源/MemoryConfigurationSourceTest.cs
@@ -0,0 +1,51 @@
+
+using OptionStudy.UnitApp.Next;
+
+namespace OptionStudy.Next
+{
+ ///
+ /// 内存 配置源
+ ///
+ public class MemoryConfigurationSourceTest : IDisposable
+ {
+ private readonly ITestOutputHelper testOutput;
+
+ public MemoryConfigurationSourceTest(ITestOutputHelper testOutputHelper)
+ {
+ this.testOutput = testOutputHelper;
+ }
+
+ ///
+ /// 使用内存配置源
+ ///
+ [Fact]
+ public void Use_Test()
+ {
+ IDictionary memoryData = new Dictionary()
+ {
+ ["AppName"] = "MemoryAppName",
+ ["AppVersion"] = "0.0.0.1",
+ ["EMail:ReceiveAddress"] = "memory@163.com",
+ ["EMail:Recipient"] = "memory",
+ };
+
+ var root = new ConfigurationBuilder().AddInMemoryCollection(memoryData).Build();
+ var configOption = root.Get();
+
+ //MemoryConfigurationProvider 可以执行添加、设置等操作
+ var provider = root.Providers.First() as MemoryConfigurationProvider;
+ provider?.Add("MyAdd", "MyValue");
+ provider?.Set("AppVersion", "2.0.0.0");
+
+ Assert.NotNull(configOption);
+ Assert.Equal("memory", configOption.EMail?.Recipient);
+
+ testOutput.WriteLine("使用 内存配置源!");
+ }
+
+ public void Dispose()
+ {
+
+ }
+ }
+}
diff --git a/OptionStudy.Next/OptionStudy.Next.csproj b/OptionStudy.Next/OptionStudy.Next.csproj
index 9fa8b26..e62b5b5 100644
--- a/OptionStudy.Next/OptionStudy.Next.csproj
+++ b/OptionStudy.Next/OptionStudy.Next.csproj
@@ -59,8 +59,4 @@
-
-
-
-
diff --git a/OptionStudy.Next/Usings.cs b/OptionStudy.Next/Usings.cs
index 88a592b..ce36fb1 100644
--- a/OptionStudy.Next/Usings.cs
+++ b/OptionStudy.Next/Usings.cs
@@ -2,7 +2,10 @@ global using System;
global using System.Linq;
global using System.Text;
global using System.Threading.Tasks;
+global using System.Collections;
global using System.Collections.Generic;
+global using System.Collections.Concurrent;
+global using System.Collections.Specialized;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.Configuration.Memory;