添加:编程体验

master
bicijinlian 2 years ago
parent 3096ecb9d2
commit 5d92327347

@ -20,6 +20,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />

@ -1,13 +1,13 @@
using System; using Microsoft.Extensions.Options;
using System.Collections.Generic; using Microsoft.Extensions.Hosting;
using System.Linq; using Microsoft.Extensions.Hosting.Internal;
using System.Text; using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using System.Threading.Tasks; using Xunit;
namespace OptionsPattern.Sutdy.Experience namespace OptionsPattern.Sutdy.Experience
{ {
/// <summary> /// <summary>
/// 6.1 Option模式 编程体验 /// 6.1 配置选项 编程体验
/// </summary> /// </summary>
public class OptionsPatternTest:IDisposable public class OptionsPatternTest:IDisposable
{ {
@ -20,7 +20,225 @@ namespace OptionsPattern.Sutdy.Experience
[Fact] [Fact]
public void Test() public void Test()
{ {
testOutput.WriteLine("Option模式 编程体验"); testOutput.WriteLine("6.1 配置选项 编程体验");
}
/// <summary>
/// 将配置绑定为 Option 对象
/// </summary>
[Fact]
public void BindConfiguration_As_OptionsObject_Test()
{
var configuration = new ConfigurationBuilder().AddJsonFile("Configs/appsettings.json",false,true).Build();
var appOption = new ServiceCollection()
.AddOptions()
.Configure<AppOption>(configuration)
.BuildServiceProvider()
.GetRequiredService<IOptions<AppOption>>().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 对象");
}
/// <summary>
/// 提供具名的 Options 对象
/// </summary>
[Fact]
public void Provide_NamedOptionsObject_Test()
{
var memoryData = new Dictionary<string, string?>()
{
["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<AppOption>("One", configuration.GetSection("One"))
.Configure<AppOption>("Two", configuration.GetSection("Two"))
.BuildServiceProvider();
var optionsSnapshot = serviceProvider.GetRequiredService<IOptionsSnapshot<AppOption>>();
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 对象");
}
/// <summary>
/// 直接初始化 Options 对象
/// (不使用IConfiguration)
/// </summary>
[Fact]
public void IniOptions_NoConfiguration_Test()
{
var appOption = new ServiceCollection()
.AddOptions()
.Configure<AppOption>(configOption =>
{
configOption.AppName = "NoConfigurationAppName";
configOption.AppVersion = new Version(0, 0, 0, 1);
configOption.EMail = new ReceiveMailOption()
{
ReceiveAddress = "NoConfigurationAppName@163.com",
Recipient = "NoConfigurationAppName",
};
})
.BuildServiceProvider()
.GetRequiredService<IOptions<AppOption>>().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 对象");
}
/// <summary>
/// 根据依赖服务的 Options 设置
/// </summary>
[Theory]
[InlineData("pro")]
[InlineData("dev")]
[InlineData("test")]
public void SetOptions_By_DependentService_Test(string environmentName)
{
var services = new ServiceCollection();
services
.AddSingleton<IHostEnvironment>(new HostingEnvironment() { EnvironmentName = environmentName })
.AddOptions<AppOption>()// AddOptions() 与 AddOptions<TOption>() 返回的类型不一样。之后的 Configure 扩展不一样
.Configure<IHostEnvironment>((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<AppOption> >()
.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 设置");
}
/// <summary>
/// 验证 Options 的有效性:通过
/// </summary>
[Fact]
public void Validate_Options_Success_Test()
{
var services = new ServiceCollection();
services
.AddOptions<AppOption>()
.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<IOptions<AppOption>>()
?.Value;
};
var exception = Record.Exception(codeSnippet);
Assert.Null(exception);
testOutput.WriteLine("验证 Options 的有效性:通过");
}
/// <summary>
/// 验证 Options 的有效性:无效并引发异常
/// </summary>
[Fact]
public void Validate_Options_Fail_Test()
{
var services = new ServiceCollection();
services
.AddOptions<AppOption>()
.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<OptionsValidationException>(() =>
{
_ = services
.BuildServiceProvider()
.GetService<IOptions<AppOption>>()
?.Value;
});
testOutput.WriteLine("验证 Options 的有效性:无效并引发异常");
} }
public void Dispose() public void Dispose()

@ -34,31 +34,16 @@ namespace OptionsPattern.Sutdy.Experience
//设置主机配置 //设置主机配置
hostBuilder.ConfigureHostConfiguration(builder => hostBuilder.ConfigureHostConfiguration(builder =>
{ {
}); });
//设置应用配置 //设置应用配置
hostBuilder.ConfigureAppConfiguration((context, builder) => hostBuilder.ConfigureAppConfiguration((context, builder) =>
{ {
}); });
//集成 Opentelemetry
//var tracerProvider = Sdk.CreateTracerProviderBuilder()
// .AddSource("Xunit.DependencyInjection")
// .AddConsoleExporter();
} }
/// <summary>
/// 配置服务方法
/// (不支持重载)
/// </summary>
//public void ConfigureServices(IServiceCollection services)
//{
//}
/// <summary> /// <summary>
/// 配置服务方法 /// 配置服务方法
/// 注入或用途 IConfiguration IHostEnvironment 请使用 context.xx; /// 注入或用途 IConfiguration IHostEnvironment 请使用 context.xx;
@ -66,7 +51,6 @@ namespace OptionsPattern.Sutdy.Experience
public void ConfigureServices(IServiceCollection services, HostBuilderContext context) public void ConfigureServices(IServiceCollection services, HostBuilderContext context)
{ {
} }
/// <summary> /// <summary>

@ -7,6 +7,8 @@ global using System.Collections.Generic;
global using System.Collections.Concurrent; global using System.Collections.Concurrent;
global using System.Collections.Specialized; global using System.Collections.Specialized;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.Configuration.Memory; global using Microsoft.Extensions.Configuration.Memory;
global using Microsoft.Extensions.Configuration.EnvironmentVariables; global using Microsoft.Extensions.Configuration.EnvironmentVariables;

Loading…
Cancel
Save