-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
347 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,345 @@ | ||
在前面的章节中,我们知道新版的MVC程序抛弃了原来的web.config文件机制,取而代替的是config.json,今天我们就来深入研究一下配置文件的相关内容。 | ||
|
||
#基本用法 | ||
新版的配置信息机制在Microsoft.Framework.ConfigurationModel命名空间下进行了重写,重写以后不仅支持XML格式,还支持json、ini、环境变量等。在模板示例程序中Startup类的构造函数内如,有如下语句: | ||
```csharp | ||
// Setup configuration sources. | ||
Configuration = new Configuration() | ||
.AddJsonFile("config.json") | ||
.AddEnvironmentVariables(); | ||
``` | ||
|
||
该语句的作用是将config.json文件以及环境变量信息加入到配置信息容器里,以便进行读取。而读取的时候则可以通过集合索引的形式或Get方法进行读取,示例如下: | ||
```csharp | ||
var path = Configuration["Path"]; | ||
var path = Configuration.Get("Path"); | ||
``` | ||
其中,多层级key键的读取,需要在多个层级名称之间用冒号分割即可,示例如下: | ||
```csharp | ||
var connString = Configuration.Get("Data:DefaultConnection:ConnectionString"); | ||
``` | ||
> 通过上述几段代码可以看出,该配置示例并不是全局实例,所以要想在别的地方也读取这些信息,就需要将该实例保存在一个全局静态变量上。 | ||
#架构设计 | ||
新的配置信息处理机制,在重写以后,更加轻量级,而且是进行跨平台使用,可以从多个数据源获取配置信息,而不必在拘泥于.config文件,而且甚至可以为不同的环境(开发、测试、生产)设置不同的配置信息。整个配置机制的各个重要实体见下图: | ||
|
||
![](http://blob.jsinh.in/media/2015/02/28/SS01-Microsoft.Framework.ConfigurationModel-Main-Entities.png) | ||
|
||
|
||
我们来一一讲述一下,这些类的具体作用: | ||
|
||
1. `IConfiguration` - 配置信息的实例接口,该接口上的`indexer`、`Get`、`TryGet`、`Set`以及其它一些像`Reload`这样的方法一起用于获取基于key/value的配置信息。 | ||
2. `IConfigurationSource` - 该接口统一了各个配置源使用时的接口方法,比如`TryGet`、`Set`以及最重要的读取配置信息的`load`方法,以便将信息加载到配置子系统里。 | ||
3. `IConfigurationSourceContainer` - 所有配置源信息的一个容器,该容器使得可以在一个单独的Configuration实例上加载各种配置源的配置信息。该接口只有一个`Add`方法用于添加基于`IConfigurationSource`的配置源信息。 | ||
4. `Configuration` - 该类实现了`IConfiguration`接口和`IConfigurationSourceContainer`接口,不保存基于key/value的所有类型的配置信息。 | ||
5. `ConfigurationExtensions` - 扩展方法,用于快速加载配置信息,如`AddCommandLine`、`AddIniFile`等。 | ||
|
||
|
||
在Microsoft.Framework.ConfigurationModel命名空间下,目前有6种不同类型的配置源类型可以使用,分别如下: | ||
|
||
1. `MemoryConfigurationSource` - 该配置源目前没有内置的add/load扩展方法(比如`AddMemoryConfiguration`),但你可以加载key/value类型的集合来实现此目的(如`IEnumerable<KeyValuePair<string, string>>`类型)。 | ||
2. `IniFileConfigurationSource` - 该配置源,可以将基于key/value格式的INI文件配置信息加载到配置系统中。 | ||
3. `CommandLineConfigurationSource` - 将程序启动时的命令行参数信息加载到配置系统中。 | ||
4. `EnvironmentVariablesConfigurationSource` - 将操作系统的环境变量信息加载到配置系统中,在Azure Website中,环境变量可以通过web界面进行设置,管理相当方便。 | ||
5. `JsonConfigurationSource` - 将json文件的信息加载配置系统。 | ||
6. `XmlconfigurationSource` - 将xml文件的信息加载到配置系统。 | ||
|
||
|
||
#详细用法 | ||
首先,由于配置系统是多实例型的,所以每次使用之前都要先声明一个示例,代码如下: | ||
```csharp | ||
IConfiguration configuration = new Configuration(); | ||
``` | ||
## 添加MemoryConfigurationSource | ||
由于在IConfigurationSourceContainer上没有为MemoryConfigurationSource定义快速加载配置信息的扩展方法,所以如果想加载这种类型的配置信息,则需要按照如下形式进行添加: | ||
```csharp | ||
((IConfigurationSourceContainer)Configuration) | ||
.Add(new MemoryConfigurationSource( | ||
new List<KeyValuePair<string, string>> { | ||
new KeyValuePair<string, string>("mem-key1", "mem-value1"), | ||
new KeyValuePair<string, string>("mem-key2", "mem-value2") | ||
})); | ||
//取值方式 | ||
var someConfiguration1 = Configuration["mem-key1"]; | ||
var someConfiguration2 = Configuration.Get("mem-key2"); | ||
``` | ||
|
||
##添加IniFileConfigurationSource | ||
IniFileConfigurationSource类型的配置信息可以通过扩展方法进行加载,代码如下: | ||
```csharp | ||
var configuration = new Configuration().AddIniFile("path\\to\\your\\configuration-ini-file.ini"); | ||
``` | ||
其中ini文件的格式模板如下: | ||
```text | ||
[ini-sec] | ||
ini-key1=value-a | ||
ini-key2=value-b | ||
[ini-sec2] | ||
ini-key1=value-c | ||
ini-key2=value-d | ||
``` | ||
这里的[ini-sec]是自定义的配置节名称,每个配置节下面可以配置多个key/value项。取值方式和基本示例中的一样,层级之间(本例是配置节和key之间)要用冒号分割,示例如下: | ||
```csharp | ||
var someConfiguration1 = Configuration["ini-sec:ini-key1"]; | ||
var someConfiguration2 = Configuration.Get("ini-sec2:ini-key2"); | ||
``` | ||
|
||
##添加CommandLineConfigurationSource | ||
在程序使用k run命名进行时传入的参数,可以通过该配置源进行读取,或者你也可以通过`AddCommandLine`扩展方法手工添加,示例如下: | ||
```csharp | ||
var configuration = new Configuration().AddCommandLine(new string[] { "key1=value1", "key2=value2", "@key3=value3" }); | ||
``` | ||
|
||
上述示例中的每个字符串都要是key/value格式,可以使用少于的特殊符号比如$、/等。 针对这些key值,你也可以使用带有`switchMappings`参数构造函数的`CommandLineConfigurationSource`类来映射某些key,`switchMappings`参数的数据类型和示例如下: | ||
```csharp | ||
var mappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) | ||
{ | ||
{ "key1", "tom1" }, | ||
{ "key2", "tom2" }, | ||
}; | ||
``` | ||
|
||
由于当前没有针对CommandLineConfigurationSource类的扩展方法,所以我们还是需要自己实例化该类,并添加到配置容器中,代码如下: | ||
```csharp | ||
((IConfigurationSourceContainer)Configuration).Add(new CommandLineConfigurationSource(commandLineArguments, switchMappings: mappings)); | ||
``` | ||
执行上述代码以后,在获取配置值的时候,如下两个key的值是一样的: | ||
```csharp | ||
var value1 = Configuration.Get("key1"); | ||
var value2 = Configuration["tom1"]; // tom1这个key的值其实就key1的值,因为tom1是key1的映射 | ||
``` | ||
|
||
> 1. 在映射的时候,新的映射key字符串里不能包括“/”字符,否则会报异常 | ||
> 2. 同样的key不能传入两次,否则也会报异常 | ||
> 3. 加载配置信息时,如果有重复key,则后一个key的值会覆盖前一个key的值。 | ||
> 4. 加载CommandLine配置信息时,如果一个key字符串以-作为前缀,那么就必须利用switchMapping将一个新key映射到旧key上,否则就会出错。 | ||
##添加EnvironmentVariablesConfigurationSource | ||
`ironmentVariablesConfigurationSource`可以将操作系统的环境变量添加到配置系统中,同时你也可以对这些环境变量进行自定义,比如在VS开发调试的时候,可以在如下界面添加一些key/value: | ||
|
||
![](http://blob.jsinh.in/media/2015/02/28/SS02-Env-Var-From-Project-Properties-In-VS2015.png) | ||
|
||
取值方式如下: | ||
```csharp | ||
var someConfiguration1 = Configuration["env_var_key1"]; | ||
var someConfiguration2 = Configuration["env_var_key2"]; | ||
``` | ||
另外,该配置源也支持Azure环境变量和连接字符串,所以你也可以在Azure界面里设置MSSQL、MYSQL、以及自定义链接字符串等等,但这些链接字符串需要以如下字符串开头: | ||
|
||
1. MySQL => `MYSQLCONNSTR_` | ||
2. MS SQL => `SQLCONNSTR_` | ||
3. SQL Azure DB => `SQLAZURECONNSTR_` | ||
4. Custom DB => `CUSTOMCONNSTR_` | ||
|
||
举例来说,定义一个开发环境的key/value如下: | ||
``` | ||
Key => SQLCONNSTR_devlocal | ||
Value => Server=localhost;Database=test_db;Trusted_Connection=True; | ||
``` | ||
通过AddEnvironmentVariables()的形式load完信息以后,我们则可以通过如下方式来访问这项信息: | ||
```csharp | ||
var connString = Configuration["Data:devlocal:ConnectionString"]; | ||
``` | ||
也就是说,在Azure里,环境变量的key会转换成Data:自定义标识符:ConnectionString这样的格式。如果你的key不是自定义key(以`CUSTOMCONNSTR_`开头)的话,你可以用如下方式获取连接字符串的provider名称,示例如下: | ||
```csharp | ||
var providerName = Configuration["Data:devlocal:ProviderName"]; | ||
/// 返回:System.Data.SqlClient | ||
``` | ||
|
||
`EnvironmentVariablesConfigurationSource`另外还提供一种前缀过滤的方式加载部分信息,比如: | ||
```csharp | ||
((IConfigurationSourceContainer)Configuration).Add(new EnvironmentVariablesConfigurationSource("Data:")); | ||
``` | ||
这样,再获取信息的时候,key值里的Data:就可以省略了,示例如下: | ||
```csharp | ||
var conn1 = Configuration["devlocal:ConnectionString"]; | ||
var conn2 = Configuration["devlocal:ProviderName"]; | ||
``` | ||
##添加JsonConfigurationSource | ||
在文章的开头,我们看到了json配置文件的加载,加载该文件只需要使用`.AddJsonFile("test.json")`扩展方法即可,但不要忘记,要先在project.json的dependencies里引用Microsoft.Framework.ConfigurationModel.Json程序集才行。 | ||
|
||
比如,如果你的config.json文件内容如下: | ||
```json | ||
{ | ||
"Data": { | ||
"DefaultConnection": { | ||
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-WebApplication1-64357659-de50-4b1e-b005-30310e7ee1ef;Trusted_Connection=True;MultipleActiveResultSets=true" | ||
} | ||
}, | ||
"EntityFramework": { | ||
"ApplicationDbContext": { | ||
"ConnectionString": "Data:DefaultConnection:ConnectionString" | ||
} | ||
} | ||
} | ||
``` | ||
那你就可以利用如下方式来访问链接字符串: | ||
```csharp | ||
var conn = Configuration["Data:DefaultConnection:ConnectionString"]; | ||
``` | ||
|
||
##添加XmlconfigurationSource | ||
`XmlconfigurationSource`配置源和JsonConfigurationSource配置源类似,首先引用Microsoft.Framework.ConfigurationModel.Xml程序集,然后调用`.AddXmlFile("test.xml")`。 | ||
|
||
如果你的配置文件test.xml的内容如下: | ||
```xml | ||
<root> | ||
<key1>Jsinh</key1> | ||
<key2 subkey2="Hello world" /> | ||
</root> | ||
``` | ||
获取形式,则稍有有些区别(会忽略根节点root): | ||
```csharp | ||
var s1 = Configuration["key1"]; // 返回Jsinh | ||
var s2 = Configuration["key2:subkey2"]; // 返回 Hello world | ||
``` | ||
但是要注意,通用的key不能重复声明,下面的文件在读取的时候就会出错。 | ||
```xml | ||
<root> | ||
<key1>Jsinh</key1> | ||
<key2 subkey2="Hello world" /> | ||
<key2 subkey2="Hello world again" /> | ||
</root> | ||
``` | ||
|
||
# 敏感信息配置(RC版新增功能) | ||
在RC版发布以后,微软又新增了一种敏感信息配置实现,程序集为`Microsoft.Framework.ConfigurationModel.UserSecrets`,通过该程序集的管理,我们可以将敏感的配置信息放在计算机的特殊目录下的`secrets.json`文件,其目录定义规则如下: | ||
``` | ||
Windows: %APPDATA%\microsoft\UserSecrets\<applicationId>\secrets.json | ||
Linux: ~/.microsoft/usersecrets/<applicationId>\secrets.json | ||
Mac: ~/.microsoft/usersecrets/<applicationId>\secrets.json | ||
``` | ||
我们来举例操作一下,首先,右键解决方案选择`Manage User Secret`,VS会自动给该程序创建一个`applicationId`,并保持在·project.json·文件中,示例如下: | ||
```json | ||
{ | ||
"userSecretsId": "aspnet5-WebDemo01-20150430014447", | ||
"webroot": "wwwroot", | ||
"version": "1.0.0-*", | ||
} | ||
``` | ||
接着会自动打开`%APPDATA%\Microsoft\UserSecrets\aspnet5-WebDemo01-20150430014447\secrets.json`文件,我们输入一个示例配置: | ||
```json | ||
{ | ||
"AA": { | ||
"BB": "CC" | ||
} | ||
} | ||
``` | ||
然后,我们在project.json文件里引用了上述程序集,再通过配置文件的统一方式进行注册,代码如下: | ||
``` | ||
Configuration = new Configuration() | ||
.AddJsonFile("config.json") | ||
.AddEnvironmentVariables() | ||
.AddUserSecrets(); // AddUserSecrets是添加敏感信息的扩展方法 | ||
``` | ||
然后就可以想普通的调用方法一下调用了,示例如下: | ||
``` | ||
var data = Configuration["AA:BB"]; // 结果:CC | ||
``` | ||
通过这种方式,我们就可以将生产环境的配置信息放在隐私的位置了。 | ||
|
||
#自定义配置源 | ||
通过以上示例以及查看其架构设计机制,我们可以发现,其实我们还可以自定义自己的配置源,比如我想从数据库中读取响应的配置信息,那我们只要定义一个DBConfigurationSource,并继承于ConfigurationSource即可,实现响应的Load重载即可。 | ||
```csharp | ||
public class DBConfigurationSource : BaseConfigurationSource | ||
{ | ||
public override void Load() | ||
{ | ||
// 读取数据库所有的key/value,并将其赋值给IDictionary<string, string>类型的Data数据 | ||
} | ||
} | ||
``` | ||
如果你不把数据保存在Data属性里,那么你还要实现如下几个重载,以便从自己的私有数据集合里获取响应的值,比如从缓存中获取,示例如下: | ||
```csharp | ||
public class DBConfigurationSource : BaseConfigurationSource | ||
{ | ||
public override void Load() | ||
{ | ||
// 读取数据库所有的key/value,保存在私有变量_data中 | ||
} | ||
|
||
public override void Set(string key, string value) | ||
{ | ||
// 更新数据库key对应的值 | ||
// base.Set(key, value); | ||
} | ||
|
||
public override bool TryGet(string key, out string value) | ||
{ | ||
// 从私有变量_data中获取key对应的value | ||
// return base.TryGet(key, out value); | ||
} | ||
|
||
public override IEnumerable<string> ProduceSubKeys(IEnumerable<string> earlierKeys, string prefix, string delimiter) | ||
{ | ||
// 私有变量_data中,根据自己的机制返回响应的SubKeys | ||
// return base.ProduceSubKeys(earlierKeys, prefix, delimiter); | ||
} | ||
} | ||
``` | ||
实现完上述类以后,再为自己创建一个扩展方法用于添加DB配置信息,代码如下: | ||
```csharp | ||
public static class CatsConfigurationExtensions | ||
{ | ||
public static IConfigurationSourceContainer AddDBConfiguration(this IConfigurationSourceContainer configuration) | ||
{ | ||
configuration.Add(new DBConfigurationSource()); | ||
return configuration; | ||
} | ||
} | ||
``` | ||
就可以通过.AddDBConfiguration()来添加DB配置源了。 | ||
|
||
> 注意,DB配置源需要使用数据库连接字符串,这一点需要注意(获取可以先从json配置文件获取连接字符串,然后再添加该配置源)。 | ||
|
||
#配置信息遍历 | ||
在默认的配置源实现中,所有的类都继承于ConfigurationSource,并且将信息数据保存在Data属性中,所以如果要遍历这些数据,则需要将其转换为ConfigurationSource类型才能使用,示例代码如下: | ||
```csharp | ||
foreach (var o in Configuration as Configuration) | ||
{ | ||
var source = o as ConfigurationSource; | ||
foreach (var key in source.Data.Keys) | ||
{ | ||
Console.WriteLine(key + ":" + source.Data[key]); | ||
} | ||
} | ||
``` | ||
|
||
#配置信息直接转换为实体类 | ||
在`IServiceCollection`接口上还有一个扩展方法`.Configure<T>`可以将类型`IConfiguration`的数据转换为一个实体类,该扩展方法的定义如下: | ||
```csharp | ||
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration config, int order = -1000, string optionsName = ""); | ||
``` | ||
举个例子,如果我们定义如下一个实体: | ||
```csharp | ||
public class AppSettings | ||
{ | ||
public string SiteTitle { get; set; } | ||
} | ||
``` | ||
然后在`config.json`里定义一个相同结构的配置信息,示例如下: | ||
```json | ||
{ | ||
"AppSettings": { | ||
"SiteTitle": "WebDemo01" | ||
} | ||
} | ||
``` | ||
那么通过在`Startup`的构造函数将配置信息加载以后,我们就可以将该信息赋值给`AppSettings`实例,代码如下: | ||
```csharp | ||
services.Configure<AppSettings>(Configuration.GetSubKey("AppSettings")); | ||
``` | ||
用的时候,使用`ApplicationServices`的`GetRequiredService`方法即可,示例如下: | ||
```csharp | ||
var appSettings = app.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Options; | ||
``` | ||
|
||
#注意事项: | ||
1. 在配置信息里,所有的key都是不区分大小写的,即key和KEY是一样的。 | ||
2. 如果多个配置源有重复的key,则以后最后添加的配置源中的key所对应的值为准。 | ||
3. `IConfiguration`下的`GetSubKeys`和`GetSubKey`可以获取某个层级(或以某个层级开头的)的所有key列表。 | ||
4. 由于`Configuration`是多实例的,所以按照示例中的代码,该实例在`Startup`里初始化以后,其它类就无法访问了,所以如果要做全局性的访问,最好在初始化之后将其保存到一个静态变量中。 | ||
|
||
> 参考1:https://github.com/aspnet/Configuration | ||
> 参考2:http://blog.jsinh.in/asp-net-5-configuration-microsoft-framework-configurationmodel/#.VSdjUpOxxzw |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters