Skip to main content

ASP.net Core中的选项模式(Options Pattern)

分类:  Asp.net Core入门 标签:  #Asp.Net core基础 #基础 #Web 发布于: 2023-06-04 20:26:19

我们上一节简要的介绍了应用如何访问存取配置,我们在存取配置时都是直接注入了IConfiguration实例,然后通过该实例的方法来访问配置,但是最佳的做法实际上是通过选项模式来访问配置。

先快速的看一下如何什么是选项模式。

我们有一个配置文件如下:

"Position": {
    "Title": "Editor",
    "Name": "Joe Smith"
  }

定义一个选项类来表示这个这个配置:

public class PositionOptions
{
    public const string Position = "Position";

    public string Title { get; set; }
    public string Name { get; set; }
}

对于选项类有如下的要求:

  • 要么使用默认的构造函数,要么定义一个public的无参构造函数。
  • 所有public的属性都要可读写.
  • 字段不会被绑定。

存取方法

定义好了配置和定义好了选项类,可以使用如下几种方式来访问配置值:

使用ConfigurationBinder.bind

public class Test22Model : PageModel
{
    private readonly IConfiguration Configuration;

    public Test22Model(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public ContentResult OnGet()
    {
        var positionOptions = new PositionOptions();
        Configuration.GetSection(PositionOptions.Position).Bind(positionOptions);

        return Content($"Title: {positionOptions.Title} \n" +
                       $"Name: {positionOptions.Name}");
    }
}

使用`ConfigurationBinder.Get

public class Test21Model : PageModel
{
    private readonly IConfiguration Configuration;
    public PositionOptions positionOptions { get; private set; }

    public Test21Model(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public ContentResult OnGet()
    {            
        positionOptions = Configuration.GetSection(PositionOptions.Position)
                                                     .Get<PositionOptions>();

        return Content($"Title: {positionOptions.Title} \n" +
                       $"Name: {positionOptions.Name}");
    }
}

使用DI注入

我们先在方法ConfigureService中配置配置:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(Configuration.GetSection(
                                        PositionOptions.Position));
    services.AddRazorPages();
}

在DI系统中配置好了之后,可以使用如下几种接口来访问:

使用IOptions<T>

public class Test2Model : PageModel
{
    private readonly PositionOptions _options;

    public Test2Model(IOptions<PositionOptions> options)
    {
        _options = options.Value;
    }

    public ContentResult OnGet()
    {
        return Content($"Title: {_options.Title} \n" +
                       $"Name: {_options.Name}");
    }
}

Tips
使用IOptions<T>, 无法在更改了配置文件之后,重新读入配置,因为IOptions<T>被注册为Singleton服务。

使用IOptionSnapshot<T>访问

public class TestSnapModel : PageModel
{
    private readonly MyOptions _snapshotOptions;

    public TestSnapModel(IOptionsSnapshot<MyOptions> snapshotOptionsAccessor)
    {
        _snapshotOptions = snapshotOptionsAccessor.Value;
    }

    public ContentResult OnGet()
    {
        return Content($"Option1: {_snapshotOptions.Option1} \n" +
                       $"Option2: {_snapshotOptions.Option2}");
    }
}

tips
注意IOptionSnapshot<T>可以即时监控配置文件的变化,因为它是scope范围的。

使用IOptionsMonitor

public class TestMonitorModel : PageModel
{
    private readonly IOptionsMonitor<MyOptions> _optionsDelegate;

    public TestMonitorModel(IOptionsMonitor<MyOptions> optionsDelegate )
    {
        _optionsDelegate = optionsDelegate;
    }

    public ContentResult OnGet()
    {
        return Content($"Option1: {_optionsDelegate.CurrentValue.Option1} \n" +
                       $"Option2: {_optionsDelegate.CurrentValue.Option2}");
    }
}

也是可以感知配置文件变化,同时支持命名选项。

什么是命名选项

当不同的配置节有使用到相同的属性名,就需要使用命名选项来区分,看下面的例子:

{
  "TopItem": {
    "Month": {
      "Name": "Green Widget",
      "Model": "GW46"
    },
    "Year": {
      "Name": "Orange Gadget",
      "Model": "OG35"
    }
  }
}

TopItem:MonthTopItem:Year中有相同的属性,Name和Model

我们可以这样类配置类:

public class TopItemSettings
{
    public const string Month = "Month";
    public const string Year = "Year";

    public string Name { get; set; }
    public string Model { get; set; }
}

配置DI容器:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<TopItemSettings>(TopItemSettings.Month,
                                       Configuration.GetSection("TopItem:Month"));
    services.Configure<TopItemSettings>(TopItemSettings.Year,
                                        Configuration.GetSection("TopItem:Year"));

    services.AddRazorPages();
}

然后使用如下的代码进行存取:

public class TestNOModel : PageModel
{
    private readonly TopItemSettings _monthTopItem;
    private readonly TopItemSettings _yearTopItem;

    public TestNOModel(IOptionsSnapshot<TopItemSettings> namedOptionsAccessor)
    {
        _monthTopItem = namedOptionsAccessor.Get(TopItemSettings.Month);
        _yearTopItem = namedOptionsAccessor.Get(TopItemSettings.Year);
    }

    public ContentResult OnGet()
    {
        return Content($"Month:Name {_monthTopItem.Name} \n" +
                       $"Month:Model {_monthTopItem.Model} \n\n" +
                       $"Year:Name {_yearTopItem.Name} \n" +
                       $"Year:Model {_yearTopItem.Model} \n"   );
    }
}

这里就是命名选项的用法。

Tips
默认情况下,ASP.net Core是感知配置文件的变化的。