在.Net core中,微软放弃了笨重基于XML的.Config配置文件(好吧,像我这种咸鱼早都忘了如何自己写一个Section了)。
现在主推新的高度可扩展的配置文件(参见此处)
对于新的配置系统,既可以通过IConfigurationSection.Value
手工一个个获取配置项的值(神烦无比,别问我怎么知道的),也可以通过选择模式(options pattern)通过POCO(Plain Old CLR Object)类读取配置(参见如下代码片段:)
//配置POJO如下:
/*
public class FooConfig
{
public string Name { get; set; }
}
*/
//加载配置
var configBuilder = new ConfigurationBuilder();
configBuilder.SetBasePath(GetConfigDir());
configBuilder.AddJsonFile("config.json");
var configuration = configBuilder.Build();
//初始化默认DI容器
var services = new ServiceCollection();
services.AddOptions();
services.Configure<FooConfig>(configuration);//从配置文件读取FooConfig
var serviceProvider = services.BuildServiceProvider();
//获取FooConfig实例
var fooConfigOptions = serviceProvider.GetService<IOptions<FooConfig>>();
var fooConfig = fooConfigOptions.Value;
var name=fooConfig.Name;
看上去一切都好,直到当FooConfig中增加了一个IEnumerable<string>
类型的属性,用来读取列表时:
public class FooConfig
{
public string Name { get; set; }
public IEnumerable<string> ValueList { get; set; }
}
跳出一个明晃晃的InvalidOperationException
。说是不支持接口类型。
那么问题来了,Microsoft.Extensions.Options究竟支持什么样的配置类?
由于现今.Net core的文档少的可怜,这个问题就只有自己翻代码求解了。
首先看这段代码:
services.Configure<FooConfig>(configuration);//从配置文件读取FooConfig
似乎,一切秘密就在这个Configure
扩展方法中?
首先查看Configure扩展方法的源码,我们又被带到了ConfigureFromConfigurationOptions
类上。那么,这个类是如何读取配置的呢?
细节请自行查看,总结下,就是调用了Microsoft.Extensions.Configuration.ConfigurationBinder.Bind
方法以绑定配置到配置类实例上(此处应有源码)。
细读代码,总结如下:
-
如果配置类中某个属性无对应配置,则不予处理(此时违反以下任何规则都没事,反正不处理)
-
配置类中所有属性的类型必须为
IConfigurationSection
或实际类型,不能为其他接口类型(interface)或抽象类(abstract clas) -
如果配置类中某个属性无公共Get方法,忽略之
-
如果配置类中某个属性无公共Set方法,且属性无值(Get方法不返回值),则忽略之(这条太细致了吧……因为属性值本身还可能可以绑定配置)
-
对于配置的叶子节点(就是
IConfigurationSection.Value
有值的那些节点),则通过TypeDescriptor.GetConverter(type).ConvertFromInvariantString(value)
转换字段(Nullable字段特殊处理,故而只要其基础类型支持转换就行) -
对于非配置叶子节点的属性,支持以下集合类型:一维数组(最普通的array)、实现了
ICollection<T>
的集合、实现了IDictionary<TKey,TValue>
的集合(TKey必须为string)、能够创建实例的类(都说了接口和抽象类不支持了嘛)
其实,如果不想使用IOptions,还可以直接Bind嘛:
var fooConfig=new FooConfig();
ConfigurationBinder.Bind(configuration,fooConfig);