四、直接初始化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. Sublime插件:增强篇

    Sublime Text 如何安装插件详见:https://packagecontrol.io/installation WordCount:可以实时显示当前文件的字数. 安装后,后下角多出字数 En ...

  2. 一个框架看懂优化算法之异同 SGD/AdaGrad/Adam

    Adam那么棒,为什么还对SGD念念不忘 (1) —— 一个框架看懂优化算法 机器学习界有一群炼丹师,他们每天的日常是: 拿来药材(数据),架起八卦炉(模型),点着六味真火(优化算法),就摇着蒲扇等着 ...

  3. PHP程序员技术职业生涯,你是如何规划的?

    职业规划是这样的 看到很多PHP程序员职业规划的文章,都是直接上来就提Linux.PHP.MySQL.Nginx.Redis.Memcache.jQuery这些,然后就直接上手搭环境.做项目,中级就是 ...

  4. JS中数组声明

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. java反斜杠替换

    java replaceAll() 方法要用 4 个反斜杠,表示一个反斜杠 例如 str1="aa\bbb"; str2="aa'bbb"; 要想替换成 str ...

  6. java 打印流

    (只有两个,PrintWriter和PrintStream) 思考:如果现在要想完成一个字符串或者是boolean型或者是字符型的数据输出使用OutputStream是否方便? 肯定是不方便的,因为O ...

  7. Codeforces Beta Round #4 (Div. 2 Only) D. Mysterious Present(LIS)

    传送门 题意: 现在我们有 n 个信封,然后我们有一张卡片,并且我们知道这张卡片的长和宽. 现给出这 n 个信封的长和宽,我们想形成一个链,这条链的长度就是这条链中所含有的信封的数量: 但是需要满足① ...

  8. P1060 梦中的统计

    题目描述 Bessie 处于半梦半醒的状态.过了一会儿,她意识到她在数数,不能入睡. Bessie的大脑反应灵敏,仿佛真实地看到了她数过的一个又一个数.她开始注意每一个数码(0..9):每一个数码在计 ...

  9. H3C 示例:计算子网内可用地址数

  10. 深度学习——RNN

    整理自: https://blog.csdn.net/woaidapaopao/article/details/77806273?locationnum=9&fps=1 原理 RNN.LSTM ...