2020-01-10
[ASP.NET Core 3框架揭秘] Options[2]: 设置选项的准确运

四、直接初始化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]: 与设置体系的整合


更多内容请关注十大外围足彩网站