DotNet Configuration 源码:https://github.com/dotnet/runtime/tree/master/src/libraries/,其中以 Microsoft.Extensions.Options 开头的项目

Options 官方文档:

Nuget包:Microsoft.Extensions.Options

建议先掌握:

  • DependencyInjection ( 依赖注入 ):
  • Configuration ( 配置 ) :

介绍

Options提供了对配置数据的强类型访问

Options提供了验证选项的机制

Configure

首先声明一个选项类

public class MyOption
{
    public string A { get; set; }

    public string B { get; set; }
}

使用Configure扩展方法对选项类进行配置

var serviceCollection = new ServiceCollection();

// 配置 MyOption
serviceCollection.Configure<MyOption>(n =>
{
    n.A = "A Value";
    n.B = "B Value";
});

var serviceProvider = serviceCollection.BuildServiceProvider();

获取IOptions<MyOption>选项服务

var myOption = serviceProvider.GetRequiredService<IOptions<MyOption>>();
MyOption myOptionValue = myOption.Value;
Console.WriteLine(myOptionValue.A);
Console.WriteLine(myOptionValue.B);

Aps.Net 中使用

配置

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.Configure<MyOption>(option =>
    {
        option.A = "A Value";
        option.B = "A Value";
    });
}

注入Ioption<MyOption>

[Route("Home")]
public class HomeController : ControllerBase
{
    private readonly MyOption _myOption;

    public HomeController(IOptions<MyOption> myOptions)
    {
        _myOption = myOptions.Value;
    }

    [HttpGet]
    public string GetAsync()
    {
        return $"A:{_myOption.A},B:{_myOption.B}";
    }
}

绑定配置,使用IConfiguration来配置选项

Nuget包:Microsoft.Extensions.Options.ConfigurationExtensions

Configure<TOptions>(IConfigureation configuration)不仅仅会绑定配置,还会添加对配置文件的更改通知,通知IOptionsMonitor重新计算选项

实例:

appsettings.json

{
  "MySetting": {
    "A": "A Value",
    "B": "B Value"
  }
}

C#

IConfiguration configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();
var serviceCollection = new ServiceCollection();
serviceCollection.Configure<MyOption>(configuration.GetSection("MySetting"));
var serviceProvider = serviceCollection.BuildServiceProvider();
var myOption = serviceProvider.GetRequiredService<IOptions<MyOption>>();
var myOptionValue = myOption.Value;
Console.WriteLine(myOptionValue.A);
Console.WriteLine(myOptionValue.B);

PostConfigure

PostConfigure 会在 Configure 之后进行配置

实例:

var serviceCollection = new ServiceCollection();
serviceCollection.PostConfigure<MyOption>(option =>
{
    option.B = "PostConfigure B Value";
});

serviceCollection.Configure<MyOption>(n =>
{
    n.A = "A Value";
    n.B = "B Value";
});

var serviceProvider = serviceCollection.BuildServiceProvider();
var myOption = serviceProvider.GetRequiredService<IOptions<MyOption>>();
MyOption myOptionValue = myOption.Value;
Console.WriteLine(myOptionValue.A); // A Value
Console.WriteLine(myOptionValue.B); // PostConfigure B Value

命名选项

可以给选项命名

默认选项名称为空字符串

IOptions 不支持获取命名选项,只能获取默认命名选项

可以使用IOptionsSnapshotIOptionsMonitor等获取命名选项

IOptionsSnapshot继承子IOptions并添加了Get(string optionName)方法来获取命名选项

IOptionsMonitorCurrentValue属性用于获取默认选项,Get(string optionName)方法来获取命名选项

实例:

var serviceCollection = new ServiceCollection();
// 配置 MyOption
serviceCollection.Configure<MyOption>(n =>
{
    n.A = "A Value";
    n.B = "B Value";
});
serviceCollection.Configure<MyOption>("My", n =>
{
    n.A = "My:A Value";
    n.B = "My:B Value";
});
var serviceProvider = serviceCollection.BuildServiceProvider();
var myOption = serviceProvider.GetRequiredService<IOptions<MyOption>>();
MyOption myOptionValue = myOption.Value;
Console.WriteLine(myOptionValue.A); // A Value
Console.WriteLine(myOptionValue.B); // B Value

// IOptionsSnapshot 获取命名选项
var myOption2 = serviceProvider.GetRequiredService<IOptionsSnapshot<MyOption>>();
MyOption myOptionValue2 = myOption2.Get("My");
Console.WriteLine(myOptionValue2.A); // My:A Value
Console.WriteLine(myOptionValue2.B); // My:B Value

// IOptionsMonitor 获取命名选项
var myOptionsMonitor = serviceProvider.GetRequiredService<IOptionsMonitor<MyOption>>();
MyOption myOptionValue3 = myOptionsMonitor.Get("My");
Console.WriteLine(myOptionValue3.A);
Console.WriteLine(myOptionValue3.B);

ConfigureAll、PostConfigureAll

ConfigureAllPostConfigureAll可以配置全部的命名配置

实例:

var serviceCollection = new ServiceCollection();
serviceCollection.ConfigureAll<MyOption>(n =>
{
    n.A = "A Value,Config By ConfigureAll";
});
// 配置 MyOption
serviceCollection.Configure<MyOption>(n =>
{
    n.B = "B Value";
});
serviceCollection.Configure<MyOption>("My", n =>
{
    n.B = "My:B Value";
});

var serviceProvider = serviceCollection.BuildServiceProvider();
var myOption = serviceProvider.GetRequiredService<IOptions<MyOption>>();
MyOption myOptionValue = myOption.Value;
Console.WriteLine(myOptionValue.A); // A Value,Config By ConfigureAll
Console.WriteLine(myOptionValue.B); // B Value

var myOption2 = serviceProvider.GetRequiredService<IOptionsSnapshot<MyOption>>();
MyOption myOptionValue2 = myOption2.Get("My");
Console.WriteLine(myOptionValue2.A); // A Value,Config By ConfigureAll
Console.WriteLine(myOptionValue2.B); // My:B Value

IOptions、IOptionsSnapshot、IOptionsMonitor

IOptions<TOptions>

  • 只会计算一次选项
  • 不支持命名选项
  • 单例服务,可以注入到任何服务生存期

IOptionsSnapshot<TOptions>

  • 会计算多次选项,每次请求时应重新计算选项
  • 注册为范围内,因此无法注入到单一实例服务
  • 支持命名选项

IOptionsMonitor<TOptions>:

  • 单例服务,可以注入到任何服务生存期
  • 支持更改通知 ( 比如配置文件更改通知 )
  • 支持命名选项
  • 支持重载配置
  • 选择性选项失效 (IOptionsMonitorCache)

IOptionsFactory<TOptions>负责创建、计算选项实例,IOptions<TOptions>IOptionsSnapshot<TOptions>IOptionsMonitor<TOptions>都使用它来创建选项实例

对比实例:

appsettings.json

{
  "MySetting": {
    "B": "B 1"
  }
}

C# Startup

public class Startup
{
    public IConfiguration Configuration { get; }

    public Startup( IConfiguration configuration)
    {
        Configuration = configuration;
    }
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.Configure<MyOption>(option => { option.A = DateTime.Now.ToString(CultureInfo.CurrentCulture); });
        services.Configure<MyOption>(Configuration.GetSection("MySetting"));
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();
        app.UseEndpoints(options => { options.MapControllers(); });
    }
}

C# HomeController

[Route("Home")]
public class HomeController : ControllerBase
{
    public IOptions<MyOption> MyOptions { get; }
    public IOptionsSnapshot<MyOption> MyOptionsSnapshot { get; }
    public IOptionsMonitor<MyOption> MyOptionsMonitor { get; }
    public IServiceProvider ServiceProvider { get; }
    public IWebHostEnvironment WebHostEnvironment { get; }

    public HomeController(
        IOptions<MyOption> myOptions,
        IOptionsSnapshot<MyOption> myOptionsSnapshot,
        IOptionsMonitor<MyOption> myOptionsMonitor,
        IServiceProvider serviceProvider,
        IWebHostEnvironment webHostEnvironment
    )
    {
        MyOptions = myOptions;
        MyOptionsSnapshot = myOptionsSnapshot;
        MyOptionsMonitor = myOptionsMonitor;
        ServiceProvider = serviceProvider;
        WebHostEnvironment = webHostEnvironment;
    }

    [HttpGet]
    public string GetAsync()
    {
        Console.WriteLine($"MyOption A:{MyOptions.Value.A}");
        Console.WriteLine($"MyOption B:{MyOptions.Value.B}");
        Console.WriteLine($"MyOptionsSnapshot A:{MyOptionsSnapshot.Value.A}");
        Console.WriteLine($"MyOptionsSnapshot B:{MyOptionsSnapshot.Value.B}");
        Console.WriteLine($"MyOptionsMonitor A:{MyOptionsMonitor.CurrentValue.A}");
        Console.WriteLine($"MyOptionsMonitor B:{MyOptionsMonitor.CurrentValue.B}");

        // 更改appsetting配置文件,处罚文件更改通知 ( json文件配置需要开启更改重载 )
        System.IO.File.WriteAllText(WebHostEnvironment.ContentRootPath + "/appsettings.json",
            JsonSerializer.Serialize(new{
                MySetting = new
                {
                    B = "B 2"
                }
            })
        );
        var timer = new Timer(2000);
        timer.Start();
        timer.Elapsed += (sender, e) =>
        {
            Console.WriteLine($"MyOption2 A:{MyOptions.Value.A}");
            Console.WriteLine($"MyOption2 B:{MyOptions.Value.B}");
            Console.WriteLine($"MyOptionsSnapshot2 A:{MyOptionsSnapshot.Value.A}");
            Console.WriteLine($"MyOptionsSnapshot2 B:{MyOptionsSnapshot.Value.B}");
            Console.WriteLine($"MyOptionsMonitor2 A:{MyOptionsMonitor.CurrentValue.A}");
            Console.WriteLine($"MyOptionsMonitor2 B:{MyOptionsMonitor.CurrentValue.B}");
            timer.Stop();
        };

        return "Hello";
    }

相关文章: