原文:https://docs.asp.net/en/latest/fundamentals/configuration.html
ASP.NET 5支持多种配置选项。 应用的配置文件可以是JSON,XML,INI格式,也可以是来自系统环境变量。而且你还可以写自己的自定义configuration provider。
获取设置configuration
ASP.NET 5的重构了之前依赖于System.Configuration和xml格式的配置文件(如web.config)。
如果你在ASP.NET应用中使用setting的话,建议你只在你应用的Startup类中实例化Configuration。然后使用Option pattern进入独立的settings。
最简单的说,Configuration就是一个source的集合,提供了读写键值对的功能。你最起码得给Configuration配置一个source。下面的代码展示如何使用Configuration做为一个键值对存储:
// assumes using Microsoft.Framework.ConfigurationModel is specified var builder = new ConfigurationBuilder(); builder.Add(new MemoryConfigurationSource()); var config = builder.Build(); config.Set("somekey", "somevalue"); // do some other work string setting = config.Get("somekey"); // returns "somevalue" // or string setting2 = config["somekey"]; // also returns "somevalue"
注意:
你至少要设置一个source。
通常我们的configuration键存在一个层级关系。ASP.NET5通过使用:号分隔来实现。例如,有如下的appsettings.json文件:
{ "Data": { "DefaultConnection": { "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-WebApplication1-8479b9ce-7b8f-4402-9616-0843bc642f09;Trusted_Connection=True;MultipleActiveResultSets=true" } }, "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Verbose", "System": "Information", "Microsoft": "Information" } } }
通过Data:DefaultConnection:ConnectionString来获取ConnectionString的值。
使用options pattern可以创建你自己的配置类,通过使用options service注入到你的应用中。
注意:
你可以将你的Configuration实例做为一个服务存储起来,但是这样以来你的系统依赖一个单一的configuration,而且所有的配置都在这个单一的configuration里面。可以使用过Options pattern 来避免这个问题。
使用built-in providers
configuration framework的built-it支持JSON, XML, 和INI配置文件,同样还支持内存configuraiton,环境变量configuratin和命令行参数的configuration。可以同时使用多种configuration source。
使用ConfigurationBuilder类来添加configuration source。支持链式写法。
var builder = new ConfigurationBuilder("."); builder.AddJsonFile("appsettings.json"); builder.AddEnvironmentVariables(); var config = builder.Build();
上面configuration source的顺序非常重要,他决定了优先级。越后添加的configuration source优先级越高。在上面的例子中,如果appsetting.json和环境变量都设置了同一个键,环境变量这个source的优先级会高,会使用环境变量中对应的键值覆盖appsettings.json中的键值。
注意:
环境变量不支持:,我们使用__(两个下划线)来代替。
针对不同的环境(debug,release,test)使用不同的配置文件非常有用。可以通过下面的例子来实现。
public Startup(IHostingEnvironment env) { // Set up configuration sources. var builder = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); if (env.IsDevelopment()) { // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709 builder.AddUserSecrets(); } builder.AddEnvironmentVariables(); Configuration = builder.Build(); }
IHostingEnviroment被用来获取当前的环境。在Development环境中,上面的高亮的代码会是appsettings.Development.json,会使用这个文件的值来覆盖其他的文件的值。更多请见Working with Multiple Environments。
警告:
你不应该在源代码或者配置文件中存储如密码一样的敏感数据。这样的敏感数据不应该在项目里面,这样你也不会意外的把他们提交到源代码仓储中去了。更多请见:Working with Multiple Environments and managing Safe Storage of Application Secrets。
下面的例子中在MemoryConfigurationSource中设置了username,但是被命令行参数的username覆盖了:
using System; using System.Linq; using Microsoft.Framework.Configuration; namespace ConfigConsole { public class Program { public void Main(string[] args) { var builder = new ConfigurationBuilder(); Console.WriteLine("Initial Config Sources: " + builder.Sources.Count()); var defaultSettings = new MemoryConfigurationSource(); defaultSettings.Set("username", "Guest"); builder.Add(defaultSettings); Console.WriteLine("Added Memory Source. Sources: " + builder.Sources.Count()); builder.AddCommandLine(args); Console.WriteLine("Added Command Line Source. Sources: " + builder.Sources.Count()); var config = builder.Build(); string username = config.Get("username"); Console.WriteLine($"Hello, {username}!"); } } }
运行结果如下:
使用Options和configuration对象
使用Options 可以很方便的把任何类转换成一个配置类。 推荐你为不同的应用模块创建对应的配置对象,不要是整个应用只有一个配置对象。
下面是一个简单的MyOptions类:
public class MyOptions { public string Option1 { get; set; } public int Option2 { get; set; } }
Options可以通过使用IOptions<TOptions>注入到你的应用。如下:
public class HomeController : Controller { public HomeController(IOptions<MyOptions> optionsAccessor) { Options = optionsAccessor.Options; } MyOptions Options { get; } public IActionResult Index() { return View(Options); } }
关于依赖注入的信息见,Dependency Injection。
为了使用IOptions<TOption>,你需要在在startup类中有如下设置:
public void ConfigureServices(IServiceCollection services) { // Setup options with DI services.AddOptions(); }
index视图显示option的值:
使用Configure<TOption>来配置你的Options。可以使用委托来配置你的options或者绑定你的options到configuration:
public void ConfigureServices(IServiceCollection services) { // Setup options with DI services.AddOptions(); // Configure MyOptions using config services.Configure<MyOptions>(Configuration); // Configure MyOptions using code services.Configure<MyOptions>(myOptions => { myOptions.Option1 = "value1_from_action"; }); services.AddMvc(); }
当你绑定options到configuration,你options的每一个属性绑定成了configuration的键(property:subproperty:....)。例如,MyOptions.Option1属性绑定到了Options1这个键,读取appsettings.json文件的options1。注意了configuration的键是大小写不敏感的。
每次调用Configuration<TOptions>都会添加一个IConfigureOptions<TOption>服务到服务容器。
对同一个option类可以有多个IConfigureOptions<TOption>,他们的优先级同样依赖于add的顺序。上面的例子中appsettings.json有Option1和Option2,但是Option1的值被下面使用委托的覆盖了。
写自定义providers
除了使用the built-in configuration source providers,我们可以自己写一个自定义的provider。需要继承自ConfigurationSource。
例子: Entity Framework Settings
你可能希望将你的配置存储在数据库里面,通过使用Entity Framework来获取他们。下面的例子,我们创建一个简单的configuration source使用EF来读取来自数据库的键值对。
首先定义ConfigurationValue实体。用来在数据库中存储配置:
public class ConfigurationValue { public string Id { get; set; } public string Value { get; set; } }
现在需要一个ConfigurationContext来做为DbContext:
public class ConfigurationContext : DbContext { public ConfigurationContext(DbContextOptions options) : base(options) { } public DbSet<ConfigurationValue> Values { get; set; } }
接下来,创建见一个继承自ConfigurationSource的configuration source。覆盖Load方法来加载配置:
public class EntityFrameworkConfigurationSource : ConfigurationSource { public EntityFrameworkConfigurationSource(Action<DbContextOptionsBuilder> optionsAction) { OptionsAction = optionsAction; } Action<DbContextOptionsBuilder> OptionsAction { get; } public override void Load() { var builder = new DbContextOptionsBuilder<ConfigurationContext>(); OptionsAction(builder); using (var dbContext = new ConfigurationContext(builder.Options)) { dbContext.Database.EnsureCreated(); Data = !dbContext.Values.Any() ? CreateAndSaveDefaultValues(dbContext) : dbContext.Values.ToDictionary(c => c.Id, c => c.Value); } } private IDictionary<string, string> CreateAndSaveDefaultValues(ConfigurationContext dbContext) { var configValues = new Dictionary<string, string> { { "key1", "value_from_ef_1" }, { "key2", "value_from_ef_2" } }; dbContext.Values.AddRange(configValues .Select(kvp => new ConfigurationValue() { Id = kvp.Key, Value = kvp.Value }) .ToArray()); dbContext.SaveChanges(); return configValues; } }
接下来,我们创建一个EntityFrameworkExtensions在里面添加IConfigurationBuilder的一个扩展方法,用于添加configuration source:
public static class EntityFrameworkExtensions { public static IConfigurationBuilder AddEntityFramework(this IConfigurationBuilder builder, Action<DbContextOptionsBuilder> setup) { return builder.Add(new EntityFrameworkConfigurationSource(setup)); } }
使用自定义provider:
public class Program { public void Main(string[] args) { var builder = new ConfigurationBuilder("."); builder.AddJsonFile("appsettings.json"); builder.AddEnvironmentVariables(); var config = builder.Build(); builder.AddEntityFramework(options => options.UseSqlServer(config["Data:DefaultConnection:ConnectionString"])); config = builder.Build(); Console.WriteLine("key1={0}", config.Get("key1")); Console.WriteLine("key2={0}", config.Get("key2")); Console.WriteLine("key3={0}", config.Get("key3")); } }
结果如下: