四、直接初始化Options对象

前面演示的几个实例具有一个共同的特征,即都采用配置系统来提供绑定Options对象的原始数据,实际上,Options框架具有一个完全独立的模型,可以称为Options模型。这个独立的Options模型本身并不依赖于配置系统,让配置系统来提供配置数据仅仅是通过Options模型的一个扩展点实现的。在很多情况下,可能并不需要将应用的配置选项定义在配置文件中,在应用启动时直接初始化可能是一种更方便快捷的方式。

class Program
{
static void Main()
{
var profile = new ServiceCollection()
.AddOptions()
.Configure<Profile>(it =>
{
it.Gender = Gender.Male;
it.Age = 18;
it.ContactInfo = new ContactInfo
{
PhoneNo = "123456789",
EmailAddress = "foobar@outlook.com"
};
})
.BuildServiceProvider()
.GetRequiredService<IOptions<Profile>>()
.Value; Console.WriteLine($"Gender: {profile.Gender}");
Console.WriteLine($"Age: {profile.Age}");
Console.WriteLine($"Email Address: {profile.ContactInfo.EmailAddress}");
Console.WriteLine($"Phone No: {profile.ContactInfo.PhoneNo}\n");
}
}

我们依然沿用前面演示的应用场景,现在摒弃配置文件,转而采用编程的方式直接对用户信息进行初始化,所以需要对程序做如上改写。在调用IServiceCollection接口的Configure<Profile>扩展方法时,不需要再指定一个IConfiguration对象,而是利用一个Action<Profile>类型的委托对作为参数的Profile对象进行初始化。程序运行后会在控制台上产生下图所示的输出结果。

具名Options同样可以采用类似的方式进行初始化。如果需要根据指定的名称对Options进行初始化,那么调用方法时就需要指定一个Action<TOptions,String>类型的委托对象,该委托对象的第二个参数表示Options的名称。在如下所示的代码片段中,我们通过类似的方式设置了两个用户(foo和bar)的信息,然后利用IOptionsSnapshot<TOptions>服务将它们分别提取出来。

class Program
{
static void Main()
{
var optionsAccessor = new ServiceCollection()
.AddOptions()
.Configure<Profile>("foo", it =>
{
it.Gender = Gender.Male;
it.Age = 18;
it.ContactInfo = new ContactInfo
{
PhoneNo = "123",
EmailAddress = "foo@outlook.com"
};
})
.Configure<Profile>("bar", it =>
{
it.Gender = Gender.Female;
it.Age = 25;
it.ContactInfo = new ContactInfo
{
PhoneNo = "456",
EmailAddress = "bar@outlook.com"
};
})
.BuildServiceProvider()
.GetRequiredService<IOptionsSnapshot<Profile>>(); Print(optionsAccessor.Get("foo"));
Print(optionsAccessor.Get("bar")); static void Print(Profile profile)
{
Console.WriteLine($"Gender: {profile.Gender}");
Console.WriteLine($"Age: {profile.Age}");
Console.WriteLine($"Email Address: {profile.ContactInfo.EmailAddress}");
Console.WriteLine($"Phone No: {profile.ContactInfo.PhoneNo}\n");
};
}
}

该程序运行后会在控制台上产生下图所示的输出结果。在前面的演示中,我们利用依赖注入框架提供IOptions<TOptions>服务、IOptionsSnapshot<TOptions>服务和IOptionsMonitor<TOptions>服务,然后进一步利用它们来提供对应的Options对象。既然作为依赖注入容器的IServiceProvider对象能够提供这3个对象,我们就能够将它们注入消费Options对象的类型中。所谓的Options模式就是通过注入这3个服务来提供对应Options对象的编程模式。

五、根据依赖服务的Options设置

在很多情况下需要针对某个依赖的服务动态地初始化Options的设置,比较典型的就是根据当前的承载环境(开发、预发和产品)对Options做动态设置。《上篇》演示了一系列针对时间日期输出格式的配置,下面沿用这个场景演示如何根据当前的承载环境设置对应的Options。将DateTimeFormatOptions的定义进行简化,只保留如下所示的表示日期和时间格式的两个属性。

public class DateTimeFormatOptions
{
public string DatePattern { get; set; }
public string TimePattern { get; set; }
public override string ToString() => $"Date: {DatePattern}; Time: {TimePattern}";
}

如下所示的代码片段是整个演示实例的完整定义。我们利用第6章介绍的配置系统来设置当前的承载环境,具体采用的是基于命令行参数的配置源。.NET Core的承载系统通过IHostEnvironment接口表示承载环境,具体实现类型为HostingEnvironment。如下面的代码片段所示,我们利用获取的环境名称创建了一个HostingEnvironment对象,并针对IHostEnvironment接口采用Singleton生命周期做了相应的注册。

class Program
{
public static void Main(string[] args)
{
var environment = new ConfigurationBuilder()
.AddCommandLine(args)
.Build()["env"]; var services = new ServiceCollection();
services
.AddSingleton<IHostEnvironment>(new HostingEnvironment { EnvironmentName = environment })
.AddOptions<DateTimeFormatOptions>().Configure<IHostEnvironment>( (options, env) => {
if (env.IsDevelopment())
{
options.DatePattern = "dddd, MMMM d, yyyy";
options.TimePattern = "M/d/yyyy";
}
else
{
options.DatePattern = "M/d/yyyy";
options.TimePattern = "h:mm tt";
}
}); var options = services
.BuildServiceProvider()
.GetRequiredService<IOptions<DateTimeFormatOptions>>().Value;
Console.WriteLine(options);
}
}

上面调用IServiceCollection接口的AddOptions<DateTimeFormatOptions>扩展方法完成了针对Options模型核心服务的注册和针对DateTimeFormatOptions的设置。该方法返回的是一个封装了IServiceCollection集合的OptionsBuilder<DateTimeFormatOptions>对象,可以调用其Configure<IHostEnvironment>方法利用提供的Action<DateTimeFormatOptions, IHostEnvironment>委托对象针对依赖的IHostEnvironment服务对DateTimeFormatOptions做相应的设置。具体来说,我们针对开发环境和非开发环境设置了不同的日期时间格式。如果采用命令行的方式启动这个应用程序,并利用命令行参数设置不同的环境名称,就可以在控制台上看到下图所示的针对DateTimeFormatOptions的不同设置。

六、验证Options的有效性

由于配置选项是整个应用的全局设置,为了尽可能避免错误的设置造成的影响,最好能够对内容进行有效性验证。接下来我们将上面的程序做了如下改动,从而演示如何对设置的日期和时间格式做最后的有效性验证。

class Program
{
public static void Main(string[] args)
{
var config = new ConfigurationBuilder()
.AddCommandLine(args)
.Build();
var datePattern = config["date"];
var timePattern = config["time"]; var services = new ServiceCollection();
services.AddOptions<DateTimeFormatOptions>()
.Configure(options =>
{
options.DatePattern = datePattern;
options.TimePattern = timePattern;
})
.Validate(options => Validate(options.DatePattern) && Validate(options.TimePattern),"Invalid Date or Time pattern.");
try
{
var options = services
.BuildServiceProvider()
.GetRequiredService<IOptions<DateTimeFormatOptions>>().Value;
Console.WriteLine(options);
}
catch (OptionsValidationException ex)
{
Console.WriteLine(ex.Message);
} static bool Validate(string format)
{
var time = new DateTime(1981, 8, 24,2,2,2);
var formatted = time.ToString(format);
return DateTimeOffset.TryParseExact(formatted, format, null, DateTimeStyles.None, out var value) && (value.Date == time.Date || value.TimeOfDay == time.TimeOfDay);
}
}
}

上述演示实例借助配置系统以命令行的形式提供了日期和时间格式化字符串。在创建了OptionsBuilder<DateTimeFormatOptions>对象并对DateTimeFormatOptions做了相应设置之后,我们调用Validate<DateTimeFormatOptions>方法利用提供的Func<DateTimeFormatOptions,bool>委托对象对最终的设置进行验证。运行该程序并按照下图所示的方式指定不同的格式化字符串,系统会根据我们指定的规则来验证其有效性。

[ASP.NET Core 3框架揭秘] Options[1]: 配置选项的正确使用方式[上篇]
[ASP.NET Core 3框架揭秘] Options[2]: 配置选项的正确使用方式[下篇]
[ASP.NET Core 3框架揭秘] Options[3]: Options模型[上篇]
[ASP.NET Core 3框架揭秘] Options[4]: Options模型[下篇]
[ASP.NET Core 3框架揭秘] Options[5]: 依赖注入
[ASP.NET Core 3框架揭秘] Options[6]: 扩展与定制
[ASP.NET Core 3框架揭秘] Options[7]: 与配置系统的整合

[ASP.NET Core 3框架揭秘] Options[2]: 配置选项的正确使用方式[下篇]的更多相关文章

  1. [ASP.NET Core 3框架揭秘] Options[1]: 配置选项的正确使用方式[上篇]

    依赖注入不仅是支撑整个ASP.NET Core框架的基石,也是开发ASP.NET Core应用采用的基本编程模式,所以依赖注入十分重要.依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式 ...

  2. 《ASP.NET Core 3框架揭秘》博文汇总

    在过去一段时间内,写了一系列关于ASP.NET Core 3相关的文章,其中绝大部分来源于即将出版的<ASP.NET Core 3框架揭秘>(博文只能算是"初稿",与书 ...

  3. ASP.NET Core 6框架揭秘实例演示[10]:Options基本编程模式

    依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式注入消费该功能的组件或者服务中.除了可以采用依赖注入的形式消费承载某种功能的服务,还可以采用相同的方式消费承载配置数据的Options对 ...

  4. [ASP.NET Core 3框架揭秘] 依赖注入[5]: 利用容器提供服务

    毫不夸张地说,整个ASP.NET Core框架是建立在依赖注入框架之上的.ASP.NET Core应用在启动时构建管道以及利用该管道处理每个请求过程中使用到的服务对象均来源于依赖注入容器.该依赖注入容 ...

  5. [ASP.NET Core 3框架揭秘] 文件系统[1]:抽象的“文件系统”

    ASP.NET Core应用 具有很多读取文件的场景,比如配置文件.静态Web资源文件(比如CSS.JavaScript和图片文件等)以及MVC应用的View文件,甚至是直接编译到程序集中的内嵌资源文 ...

  6. [ASP.NET Core 3框架揭秘] 配置[1]:读取配置数据[上篇]

    提到"配置"二字,我想绝大部分.NET开发人员脑海中会立即浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化 ...

  7. [ASP.NET Core 3框架揭秘] 配置[2]:读取配置数据[下篇]

    [接上篇]提到“配置”二字,我想绝大部分.NET开发人员脑海中会立即浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置定义 ...

  8. [ASP.NET Core 3框架揭秘] 配置[6]:多样化的配置源[上篇]

    .NET Core采用的这个全新的配置模型的一个主要的特点就是对多种不同配置源的支持.我们可以将内存变量.命令行参数.环境变量和物理文件作为原始配置数据的来源.如果采用物理文件作为配置源,我们可以选择 ...

  9. [ASP.NET Core 3框架揭秘] 依赖注入[10]:与第三方依赖注入框架的适配

    .NET Core具有一个承载(Hosting)系统,承载需要在后台长时间运行的服务,一个ASP.NET Core应用仅仅是该系统承载的一种服务而已.承载系统总是采用依赖注入的方式来消费它在服务承载过 ...

随机推荐

  1. win10如何关闭计算机设备和驱动器非硬盘图标

    按win键+R,打开注册表regedit,找到这个路径: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\My ...

  2. canvas简单动画

    实现场景:定义一个1000*800的方框,圆球在其中移动,碰撞到边框弹回的动画.方框背景是半径为10的小圆球组成.鼠标移动到移动圆球时,圆球停止运动. html代码: <div> < ...

  3. Google Colab 免费GPU服务器使用教程 挂载云端硬盘

    一.前言二.Google Colab特征三.开始使用3.1在谷歌云盘上创建文件夹3.2创建Colaboratory3.3创建完成四.设置GPU运行五.运行.py文件5.1安装必要库5.2 挂载云端硬盘 ...

  4. NIO 中文乱码问题的解决代码实现

    之前在网上查询了很多关于解决NIO中文乱码的问题,仁者见仁智者见智,不过就找到的几种方法实现都太繁琐了,稍微研究了下NIO源码,以下是我自己的一种实现,偷懒用最简单的代码去实现是我的习惯! Demo: ...

  5. lrj 9.2.3

    <<); // 记忆化搜索 min[] = ; int dp(int i) { ) return maxv[i]; maxv[i] = -INF; ; j <= n; j++) if ...

  6. hibernate无限递归问题

    项目异常如下: 2018-01-26 17:12:38.162 WARN 3128 --- [nio-8080-exec-6] .w.s.m.s.DefaultHandlerExceptionReso ...

  7. C# 标准性能测试

    经常我写一个类,作为一个工具类,小伙伴会问我这个类的性能,这时我就需要一个标准的工具进行测试. 本文告诉大家如何使用 benchmarkdotnet 做测试. 现在在 github 提交代码,如果有小 ...

  8. spring json 返回中文乱码

    如前台显示的json数据中的中文为???,则可尝试以下方法. 方法一(推荐):在@RequestMapping中添加  produces={"text/html;charset=UTF-8; ...

  9. 51nod 1307绳子和重物

    1307 绳子与重物  题目来源: Codility 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题  收藏  关注 有N条绳子编号 0 至 N - 1,每条绳子后 ...

  10. Linux 内核设备注册

    通常的注册和注销函数在: int device_register(struct device *dev); void device_unregister(struct device *dev); 我们 ...