原文:Configuration
作者:Steve Smith、Daniel Roth
翻譯:劉怡(AlexLEWIS)
校對:孟帥洋(書緣)
ASP.NET Core 支持多種配置選項。應用程序配置數據內建支持讀取 JSON、XML 和 INI 格式的配置文件和環境變量。你也可以編寫自己的自定義配置提供程序。
章節:
獲取和設置配置
ASP.NET Core 配置系統針對以前的 ASP.NET 版本(依賴於 System.Configuration
和 XML 配置文件(如 Web.config
))進行了重新架構。新的配置模型提供了精簡高效的通過檢索多樣化提供程序的獲取基於鍵/值對配置的能力。應用程序和框架可以通過新的選擇模式訪問配置。
在 ASP.NET 應用程序中,建議你在應用程序的 Startup
類中只實例化一個 Configuration
實例。然后使用選擇模式來訪問各自的設置。
簡單來說,Configuration
類只是一個提供了讀寫名/值對能力的 Providers
集合。你至少需要配置一個提供程序,使得 Configuration
能正常工作。下例演示了如何測試把 Configuration
作為一個鍵/值對存儲來處理。
var builder = new ConfigurationBuilder();
builder.AddInMemoryCollection();
var config = builder.Build();
config["somekey"] = "somevalue";
// do some other work
var setting = config["somekey"]; // also returns "somevalue"
注意
你必須至少設置一個配置提供程序。
一般不會把配置值存儲在一個有層次的結構中,尤其是使用外部文件(如 JSON、XML、INI)時。在此情況下,可以使用以 :
符號分隔(從層次結構的根開始)的鍵來取回配置值。以下面的 appsettings.json 文件為例:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-WebApplication1-26e8893e-d7c0-4fc6-8aab-29b59971d622;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
應用程序使用 configuration 配置正確的連接字符串。可以通過鍵 Data:DefaultConnection:ConnectionString
來訪問 ConnectionString
的設置。
應用程序所需要的設置和指定配置的機制(configuration 便是一例)都可通過使用選擇模式解耦。創建自己的配置類(可以是幾個不同的類,分別對應不同的配置組),而后通過選項服務注入到應用程序中。然后你就可以通過配置或其它你所選擇的機制來設置了。
注意
你可將Configuration
實例設計為一個服務,但這會導致不必要地把應用程序和配置系統與指定配置鍵耦合在一起。相反可通過選擇模式來避免這一問題。
使用內建提供程序
配置框架已內建支持 JSON、XML 和 INI 配置文件,內存配置(直接通過代碼設置值),從環境變量和命令行參數中拉取配置。開發者並不受限於必須使用單個配置提供程序。事實上可以把多個配置提供程序組合在一起,就像是用從當前存在的另一個配置提供程序中獲取配置值覆蓋默認配置一樣。
擴展方法支持為配置添加額外的配置文件提供程序。這些方法能被獨立的或鏈式的(如 fluent API)調用在 ConfigurationBuilder
實例之上,如下所示:
// work with with a builder using multiple calls
var builder = new ConfigurationBuilder();
builder.SetBasePath(Directory.GetCurrentDirectory());
builder.AddJsonFile("appsettings.json");
var connectionStringConfig = builder.Build();
// chain calls together as a fluent API
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddEntityFrameworkConfig(options =>
options.UseSqlServer(connectionStringConfig.GetConnectionString("DefaultConnection"))
)
.Build();
指定配置提供程序的順序非常重要,這將影響它們的設置被應用的優先級(如果存在於多個位置)。上例中,如果相同的配置同時存在於 appsettings.json 和環境變量,則環境變量的設置將被最終使用。最后指定的配置提供程序將“獲勝”(如果該設置存在於至少兩處位置)。ASP.NET 團隊建議最后指定環境變量,如此一來本地環境可以覆蓋任何部署在配置文件中的設置。
注意
如果通過環境變量重寫嵌套鍵,請把變量中鍵名的:
替換為__
(兩個下划線)。
這對於指定環境的配置文件非常有用,這能通過以下代碼來實現:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.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();
}
IHostingEnvironment
服務用於獲取當前環境。在 Development
環境中,上例高亮行代碼將尋找名為 appsettings.Development.json
的配置文件,並用其中的值覆蓋當前存在的其它值。更多請參見 environments。
警告
謹記,嚴禁把密碼或其他敏感數據保存在代碼或純文本配置文件中,嚴謹在開發環境或測試環境中使用生產環境的機密數據(這些機密數據應當在項目樹的外部被指定,這樣就不會意外提交到倉庫內)。移步了解更多 environments 和管理 Safe storage of app secrets during development。
影響 Configuration
優先級順序的一個因素是指定可被重寫的默認值。在本控制台應用程序中,默認的 username
設置由 MemoryConfigurationProvider
指定,但如果命令行參數中有個 username
參數被傳遞給應用程序,它將被覆蓋。在輸出中可以看到程序的每一個步驟中有多少個配置提供程序在進行配置工作。
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Configuration;
namespace ConfigConsole
{
public static class Program
{
public static void Main(string[] args)
{
var builder = new ConfigurationBuilder();
Console.WriteLine("Initial Config Sources: " + builder.Sources.Count());
builder.AddInMemoryCollection(new Dictionary<string, string>
{
{ "username", "Guest" }
});
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["username"];
Console.WriteLine($"Hello, {username}!");
}
}
}
當運行時,程序將顯示默認值,除非使用命令行參數重寫之。
使用選項和配置對象
通過使用選擇模式(options pattern)你可將任何類或 POCO(Plain Old CLR Object)轉換為設置類。推薦把你創建的配置根據應用程序的功能分解為多個配置對象,從而實現 ISP(Interface Segregation Principle,接口隔離原則,類只依賴於它們自己使用的配置設置)和 SoC(Separation of Concerns,關注分離,設置與應用程序相互隔離,減少彼此之間的干擾和影響)。
一個簡單的 MyOptions
類如下所示:
public class MyOptions
{
public string Option1 { get; set; }
public int Option2 { get; set; }
}
通過 IOptions<TOptions>
,配置選項將被注入到應用程序中。比方說,如 controller 使用 IOptions<TOptions>
來訪問需要在 Index
視圖中渲染的配置:
public class HomeController : Controller
{
private readonly IOptions<MyOptions> _optionsAccessor; //手動高亮
public HomeController(IOptions<MyOptions> optionsAccessor) //手動高亮
{
_optionsAccessor = optionsAccessor;
} //手動高亮
// GET: /<controller>/
public IActionResult Index() => View(_optionsAccessor.Value);
}
建議
更多請瀏覽 Dependency Injection。
為設置 IOptions<TOption>
服務,你需在啟動期間在 ConfigureServices
方法內調用 AddOptions()
擴展方法。
public void ConfigureServices(IServiceCollection services)
{
// Setup options with DI
services.AddOptions();//手動高亮
Index
視圖將顯示配置選項:
配置選項使用 Configure<TOption>
擴展方法。你可以通過委托或綁定配置選項的方式來進行配置:
public void ConfigureServices(IServiceCollection services)
{
// Setup options with DI
services.AddOptions(); //手動高亮
// Configure MyOptions using config by installing Microsoft.Extensions.Options.ConfigurationExtensions
services.Configure<MyOptions>(Configuration);
// Configure MyOptions using code
services.Configure<MyOptions>(myOptions => //手動高亮
{
myOptions.Option1 = "value1_from_action"; //手動高亮
}); //手動高亮
// Configure MySubOptions using a sub-section of the appsettings.json file
services.Configure<MySubOptions>(Configuration.GetSection("subsection"));//手動高亮
// Add framework services.
services.AddMvc();
}
當你通過綁定選項來配置選項類型的每一個屬性,實際上是綁定到每一個配置鍵(比如 property:subproperty:...
)。比方說,MyOptions.Option1
屬性綁定到鍵 Option1
,那么就會從 appsettings.json 中讀取 option1
屬性。注意,配置鍵是大小寫不敏感的。
通過調用 Configure<TOption>
將一個 IConfigureOptions<TOption>
服務加入服務容器,是為了之后應用程序或框架能通過 IOptions<TOption>
服務來獲取配置選項。若是想從其他途徑(比如之前從數據庫)獲取配置,你可使用 ConfigureOptions<TOptions>
擴展方法直接指定經過定制的 IConfigureOptions<TOption>
服務。
同一個選項類型可以有多個 IConfigureOptions<TOption>
服務,屆時將按順序應用。在上例中,Option1 和 Option2 都在 appsettings.json
中指定,但 Option1 的值最后被配置委托所覆蓋。
編寫自定義提供程序
除使用內建的配置提供程序,你也可自行定制。為此,你只需從 ConfigurationProvider
繼承並使用來自你配置提供程序的配置來填充 Data
屬性即可。
例子:Entity Framework 設置
你或許希望將應用程序的配置保存在數據庫中,然后通過 EntityFramework(EF)來訪問它們。保存這些配置值有很多辦法可以選擇,比方說一張簡易表格,一列表示配置名、另一列表示配置的值。在本例中,我將創建一個簡易的配置提供程序,通過 EF 從數據庫中讀取名值對(name-value pair)。
在開始之前我們先定義一個簡單的 ConfigurationValue
實體模型用來表示存儲在數據庫中的配置值。
public class ConfigurationContext : DbContext
{
public ConfigurationContext(DbContextOptions options) : base(options)
{
}
public DbSet<ConfigurationValue> Values { get; set; } //手動高亮
}
然后需要一個 ConfigurationContext
用來通過 EF 存儲和訪問配置值
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace CustomConfigurationProvider
{
public class EntityFrameworkConfigurationSource : IConfigurationSource //手動高亮
{
private readonly Action<DbContextOptionsBuilder> _optionsAction;
public EntityFrameworkConfigurationSource(Action<DbContextOptionsBuilder> optionsAction)
{
_optionsAction = optionsAction;
}
public IConfigurationProvider Build(IConfigurationBuilder builder) //手動高亮
{ //手動高亮
return new EntityFrameworkConfigurationProvider(_optionsAction); //手動高亮
} //手動高亮
}
}
接着,通過繼承 ConfigurationProvider
創建一個定制的配置提供程序。配置數據由重寫的 Load
方法(該方法將從配置數據庫中讀取所有配置數據)所提供。由於這是演示,所以配置提供程序需要自行初始化數據庫(如果該數據庫尚未創建並初始化)
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace CustomConfigurationProvider
{
public class EntityFrameworkConfigurationProvider : ConfigurationProvider //手動高亮
{
public EntityFrameworkConfigurationProvider(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 static 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;
}
}
}
按慣例,我們同樣可以添加一個 AddEntityFramework
擴展方法來增加配置提供程序:
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace CustomConfigurationProvider
{
public static class EntityFrameworkExtensions
{
public static IConfigurationBuilder AddEntityFrameworkConfig( //手動高亮
this IConfigurationBuilder builder, Action<DbContextOptionsBuilder> setup)
{
return builder.Add(new EntityFrameworkConfigurationSource(setup));
}
}
}
在下例中將演示如何在應用程序中使用此定制的 ConfigurationProvider
。創建一個新的 [ConfigurationBuilder][ConfigurationBuilder1]
來設置配置提供程序。指定數據提供程序和連接字符串后,添加 EntityFrameworkConfigurationProvider
配置提供程序。那你如何配置連接字符串呢?當然也是使用配置了!添加 appsettings.json 作為配置提供者來引導建立 EntityFrameworkConfigurationProvider
。通過重用相同的 ConfigurationBuilder
,在數據庫中指定的任何配置都將覆蓋 appsettings.json 中所指定的對應設置:
using System;
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace CustomConfigurationProvider
{
public static class Program
{
public static void Main()
{
// work with with a builder using multiple calls
var builder = new ConfigurationBuilder();
builder.SetBasePath(Directory.GetCurrentDirectory());
builder.AddJsonFile("appsettings.json");
var connectionStringConfig = builder.Build();
// chain calls together as a fluent API
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json") //手動高亮
.AddEntityFrameworkConfig(options => //手動高亮
options.UseSqlServer(connectionStringConfig.GetConnectionString("DefaultConnection")) //手動高亮
) //手動高亮
.Build();
Console.WriteLine("key1={0}", config["key1"]);
Console.WriteLine("key2={0}", config["key2"]);
Console.WriteLine("key3={0}", config["key3"]);
}
}
}
運行程序,看到所配置的值。
總結
ASP.NET Core 提供了非常靈活的配置模型,支持多種配置文件類型、命令行、內存和環境變量。它能與配置模型無縫協作,因此你可為你的應用程序或框架注入強類型配置。你也可以創建自己定制的配置提供程序,用於協同或取代內置提供程序,保證了最大程序的靈活性。