ASP.NET Core: ConfigurationBuilder
在 ASP.NET Core 中,大量使用了建造模式 Builer,从类型的名称就可以看出来这一点,例如 HostBuilder、ConfigurationBuilder 等等。
建造模式是对象的创建模式,建造模式可以将一个产品的内部实现与产品的生成过程分离开来。从而可以使一个建造过程生成具有不同的内部表象的产品对象。使用建造模式可以使客户端不需要知道所生成的产品对象有哪些零件,每个产品的对应零件彼此有何不同,是怎样建造出来的,以及怎样组成产品。
建造模式的 UML 如下所示:
秒懂设计模式之建造者模式(Builder pattern 对建造模式有深入的说明。
下面以 Host 的 Configuration 为例进行说明。
Host 中内部的 BuildHostConfiguration() 用来构建宿主 Host 的配置信息,它又是如何与提供给用户的扩展接口 IHostBuilder ConfigureHostConfiguration(Action
在 HostBuilder 内部,定义了一个用来保存宿主配置信息的字段 _hostConfiguration,它是实际的配置对象。但是,我们只是在内部使用它,而不把它暴露出来供用户直接配置。
private IConfiguration _hostConfiguration;
我们可以先看一看在 HostBuilder 内部的 BuildHostConfiguration() 方法实现,在 GitHub 中查看 HostBuilder 源码。
private void BuildHostConfiguration()
{
var configBuilder = new ConfigurationBuilder()
.AddInMemoryCollection(); // Make sure there's some default storage since there are no default providers
foreach (var buildAction in _configureHostConfigActions)
{
buildAction(configBuilder);
}
_hostConfiguration = configBuilder.Build();
}
可以看到,内部创建了一个 ConfigurationBuilder 的实例,然后将它作为参数,依此调用我们提供的 Action
我们的代码实际上是通过处理这个 IConfigurationBuilder 来进行配置的。这样在配置的时候,是通过这个暴露出来的 IConfigurationBuilder 来进行配置的。
IConfigurationBuilder
ConfigurationBuilder 实现了 IConfigurationBuilder 接口,在 GitHub 中查看 IConfigurationBuilder 源码。这个接口提供了管理配置的方法和属性。同时,还可以通过它的扩展方法进行配置。
public interface IConfigurationBuilder {
/// <summary>
/// Gets a key/value collection that can be used to share data between the <see cref="IConfigurationBuilder"/>
/// and the registered <see cref="IConfigurationSource"/>s.
/// </summary>
IDictionary<string, object> Properties { get; }
/// <summary>
/// Gets the sources used to obtain configuration values
/// </summary>
IList<IConfigurationSource> Sources { get; }
/// <summary>
/// Adds a new configuration source.
/// </summary>
IConfigurationBuilder Add (IConfigurationSource source);
/// <summary>
/// Builds an <see cref="IConfiguration"/> with keys and values from the set of sources registered in
/// <see cref="Sources"/>.
/// </summary>
IConfigurationRoot Build ();
}
ConfigurationBuilder
在 GitHub 中查看 ConfigurationBuilder 源码。
namespace Microsoft.Extensions.Configuration {
public class ConfigurationBuilder : IConfigurationBuilder {
public IList<IConfigurationSource> Sources { get; } = new List<IConfigurationSource> ();
public IDictionary<string, object> Properties { get; } = new Dictionary<string, object> ();
public IConfigurationBuilder Add (IConfigurationSource source) {
if (source == null) {
throw new ArgumentNullException (nameof (source));
}
Sources.Add (source);
return this;
}
public IConfigurationRoot Build () {
var providers = new List<IConfigurationProvider> ();
foreach (IConfigurationSource source in Sources) {
IConfigurationProvider provider = source.Build (this);
providers.Add (provider);
}
return new ConfigurationRoot (providers);
}
}
}
应用
在 Host 中,我们看到过这样的代码,在 GitHub 中查看 Host 源码。
builder.ConfigureHostConfiguration(config =>
{
config.AddEnvironmentVariables(prefix: "DOTNET_");
if (args != null)
{
config.AddCommandLine(args);
}
});
这个 AddEnvironmentVariables() 是一个扩展方法,在 GitHub 中查看 EnvironmentVariablesExtensions 源码。
public static IConfigurationBuilder AddEnvironmentVariables(
this IConfigurationBuilder configurationBuilder,
string prefix)
{
configurationBuilder.Add(new EnvironmentVariablesConfigurationSource { Prefix = prefix });
return configurationBuilder;
}
最终调用了 ConfigurationBuilder 的 Add() 方法。
需要注意的是,这个方法最终返回了 configurationBuilder 对象本身,所以,我们可以使用链式调用来进行进一步的配置。