diff --git a/OptionsPattern.Sutdy.Experience/OptionsPattern.Sutdy.Experience.csproj b/OptionsPattern.Sutdy.Experience/OptionsPattern.Sutdy.Experience.csproj index 5ee861d..95983b8 100644 --- a/OptionsPattern.Sutdy.Experience/OptionsPattern.Sutdy.Experience.csproj +++ b/OptionsPattern.Sutdy.Experience/OptionsPattern.Sutdy.Experience.csproj @@ -20,6 +20,7 @@ + diff --git a/OptionsPattern.Sutdy.Experience/OptionsPatternTest.cs b/OptionsPattern.Sutdy.Experience/OptionsPatternTest.cs index d4f8906..d6dabe5 100644 --- a/OptionsPattern.Sutdy.Experience/OptionsPatternTest.cs +++ b/OptionsPattern.Sutdy.Experience/OptionsPatternTest.cs @@ -1,13 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Hosting.Internal; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Xunit; namespace OptionsPattern.Sutdy.Experience { /// - /// 6.1 Option模式 编程体验 + /// 6.1 配置选项 编程体验 /// public class OptionsPatternTest:IDisposable { @@ -20,7 +20,225 @@ namespace OptionsPattern.Sutdy.Experience [Fact] public void Test() { - testOutput.WriteLine("Option模式 编程体验"); + testOutput.WriteLine("6.1 配置选项 编程体验"); + } + + /// + /// 将配置绑定为 Option 对象 + /// + [Fact] + public void BindConfiguration_As_OptionsObject_Test() + { + var configuration = new ConfigurationBuilder().AddJsonFile("Configs/appsettings.json",false,true).Build(); + + var appOption = new ServiceCollection() + .AddOptions() + .Configure(configuration) + .BuildServiceProvider() + .GetRequiredService>().Value; + + Assert.NotNull(configuration); + Assert.NotNull(appOption); + + Assert.Equal("JsonAppNmae", appOption.AppName); + Assert.Equal(new Version(0,0,0,1), appOption.AppVersion); + Assert.Equal("json@163.com", appOption.EMail?.ReceiveAddress); + Assert.Equal("json", appOption.EMail?.Recipient); + + testOutput.WriteLine("将配置绑定为 Option 对象"); + } + + /// + /// 提供具名的 Options 对象 + /// + [Fact] + public void Provide_NamedOptionsObject_Test() + { + var memoryData = new Dictionary() + { + ["One:AppName"] = "OneAppName", + ["One:AppVersion"] = "1.1.1.1", + ["One:EMail:ReceiveAddress"] = "One@163.com", + ["One:EMail:Recipient"] = "One", + + ["Two:AppName"] = "TwoAppName", + ["Two:AppVersion"] = "2.2.2.2", + ["Two:EMail:ReceiveAddress"] = "Two@163.com", + ["Two:EMail:Recipient"] = "Two", + }; + + var configuration = new ConfigurationBuilder().AddInMemoryCollection(memoryData).Build(); + + var serviceProvider = new ServiceCollection() + .AddOptions() + .Configure("One", configuration.GetSection("One")) + .Configure("Two", configuration.GetSection("Two")) + .BuildServiceProvider(); + + var optionsSnapshot = serviceProvider.GetRequiredService>(); + + var one = optionsSnapshot.Get("One"); + var two = optionsSnapshot.Get("Two"); + + Assert.NotNull(one); + Assert.NotNull(two); + + Assert.Equal("OneAppName", one.AppName); + Assert.Equal(new Version(1,1,1,1), one.AppVersion); + Assert.Equal("One@163.com", one.EMail?.ReceiveAddress); + Assert.Equal("One", one.EMail?.Recipient); + + Assert.Equal("TwoAppName", two.AppName); + Assert.Equal(new Version(2, 2, 2, 2), two.AppVersion); + Assert.Equal("Two@163.com", two.EMail?.ReceiveAddress); + Assert.Equal("Two", two.EMail?.Recipient); + + testOutput.WriteLine("提供具名的 Options 对象"); + } + + /// + /// 直接初始化 Options 对象 + /// (不使用IConfiguration) + /// + [Fact] + public void IniOptions_NoConfiguration_Test() + { + var appOption = new ServiceCollection() + .AddOptions() + .Configure(configOption => + { + configOption.AppName = "NoConfigurationAppName"; + configOption.AppVersion = new Version(0, 0, 0, 1); + configOption.EMail = new ReceiveMailOption() + { + ReceiveAddress = "NoConfigurationAppName@163.com", + Recipient = "NoConfigurationAppName", + }; + }) + .BuildServiceProvider() + .GetRequiredService>().Value; + + Assert.NotNull(appOption); + + Assert.Equal("NoConfigurationAppName", appOption.AppName); + Assert.Equal(new Version(0, 0, 0, 1), appOption.AppVersion); + Assert.Equal("NoConfigurationAppName@163.com", appOption.EMail?.ReceiveAddress); + Assert.Equal("NoConfigurationAppName", appOption.EMail?.Recipient); + + testOutput.WriteLine("直接初始化 Options 对象"); + } + + /// + /// 根据依赖服务的 Options 设置 + /// + [Theory] + [InlineData("pro")] + [InlineData("dev")] + [InlineData("test")] + public void SetOptions_By_DependentService_Test(string environmentName) + { + var services = new ServiceCollection(); + services + .AddSingleton(new HostingEnvironment() { EnvironmentName = environmentName }) + .AddOptions()// AddOptions() 与 AddOptions() 返回的类型不一样。之后的 Configure 扩展不一样 + .Configure((appOption, hostEnv) => // 泛型参数确定 Action 参数 + { + appOption.AppName = hostEnv.EnvironmentName + nameof(appOption.AppName); + appOption.AppVersion = new Version(); + appOption.EMail = new ReceiveMailOption() + { + ReceiveAddress = $"{hostEnv.EnvironmentName}@163.com", + Recipient = hostEnv.EnvironmentName, + }; + }); + var appOption = services + .BuildServiceProvider() + .GetRequiredService< IOptions >() + .Value; + + + Assert.NotNull(appOption); + Assert.Equal($"{environmentName}AppName",appOption.AppName); + Assert.Equal(new Version(),appOption.AppVersion); + Assert.Equal($"{environmentName}@163.com",appOption.EMail?.ReceiveAddress); + Assert.Equal($"{environmentName}",appOption.EMail?.Recipient); + + testOutput.WriteLine("根据依赖服务的 Options 设置"); + } + + /// + /// 验证 Options 的有效性:通过 + /// + [Fact] + public void Validate_Options_Success_Test() + { + var services = new ServiceCollection(); + services + .AddOptions() + .Configure(configOption => + { + configOption.AppName = "ValidateAppName"; + configOption.AppVersion = new Version(0, 0, 0, 1); + configOption.EMail = new ReceiveMailOption() + { + ReceiveAddress = "Validate@163.com", + Recipient = "Validate", + }; + }) + .Validate(option => + { + return true; + }); + + //不发生异常 + Action codeSnippet = () => + { + _ = services + .BuildServiceProvider() + .GetService>() + ?.Value; + }; + + var exception = Record.Exception(codeSnippet); + Assert.Null(exception); + + testOutput.WriteLine("验证 Options 的有效性:通过"); + } + + /// + /// 验证 Options 的有效性:无效并引发异常 + /// + [Fact] + public void Validate_Options_Fail_Test() + { + var services = new ServiceCollection(); + services + .AddOptions() + .Configure(configOption => + { + configOption.AppName = "ValidateAppName"; + configOption.AppVersion = new Version(0, 0, 0, 1); + configOption.EMail = new ReceiveMailOption() + { + ReceiveAddress = "Validate@163.com", + Recipient = "Validate", + }; + }) + .Validate(option => //返回false,表示验证错误,会抛出异常 + { + return false; + }); + + //引发异常 + Assert.Throws(() => + { + _ = services + .BuildServiceProvider() + .GetService>() + ?.Value; + }); + + testOutput.WriteLine("验证 Options 的有效性:无效并引发异常"); } public void Dispose() diff --git a/OptionsPattern.Sutdy.Experience/Startup.cs b/OptionsPattern.Sutdy.Experience/Startup.cs index 04de861..6e95ca1 100644 --- a/OptionsPattern.Sutdy.Experience/Startup.cs +++ b/OptionsPattern.Sutdy.Experience/Startup.cs @@ -34,31 +34,16 @@ namespace OptionsPattern.Sutdy.Experience //设置主机配置 hostBuilder.ConfigureHostConfiguration(builder => { - + }); //设置应用配置 hostBuilder.ConfigureAppConfiguration((context, builder) => { - + }); - - //集成 Opentelemetry - //var tracerProvider = Sdk.CreateTracerProviderBuilder() - // .AddSource("Xunit.DependencyInjection") - // .AddConsoleExporter(); - } - /// - /// 配置服务方法 - /// (不支持重载) - /// - //public void ConfigureServices(IServiceCollection services) - //{ - - //} - /// /// 配置服务方法 /// 注入或用途 IConfiguration IHostEnvironment 请使用 context.xx; @@ -66,7 +51,6 @@ namespace OptionsPattern.Sutdy.Experience public void ConfigureServices(IServiceCollection services, HostBuilderContext context) { - } /// diff --git a/OptionsPattern.Sutdy.Experience/Usings.cs b/OptionsPattern.Sutdy.Experience/Usings.cs index ce36fb1..7c4f0e9 100644 --- a/OptionsPattern.Sutdy.Experience/Usings.cs +++ b/OptionsPattern.Sutdy.Experience/Usings.cs @@ -7,6 +7,8 @@ global using System.Collections.Generic; global using System.Collections.Concurrent; global using System.Collections.Specialized; +global using Microsoft.Extensions.DependencyInjection; + global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.Configuration.Memory; global using Microsoft.Extensions.Configuration.EnvironmentVariables;