ç®å½
- ä¸ã建é è 模å¼ï¼Builder Patternï¼
- äºãæ ¸å¿æ¥å£ä¸é ç½®åå¨æ¬è´¨
- ä¸ãç®æQueryStringé ç½®æºå®ç°
- åã宿主é ç½®ä¸åºç¨é ç½®
ä¸ã建é è 模å¼
为ä»ä¹æ建é è 模å¼ï¼å¨é 读.NET Coreæºç æ¶ï¼æ¶å¸¸ç¢°å°IHostBuilderï¼IConfigurationBuilderï¼ILoggerBuilderç诸å¦æ¤ç±»å¸¦Builderå称çç±»/æ¥å£ï¼èµ·åä¸ç æ¶é£æ¯ä¸å¤´æ£ãç¥è¯ä¸å¤ï¼å¤å¥æ¥åï¼å¨äºè§£å°Builder模å¼åç»äºç解ï¼æç½è¿äºBuilderç±»æ¯ç¨æ¥æ建ç¸å¯¹åºç±»ç对象ï¼ç¨å®å³æ¯å«æ ä»ç¨ãç解建é è 模å¼ï¼æå©äºé 读æºç æ¶åç°æ ¸å¿æ¥å£/ç±»ï¼å°æ件åç±»ï¼ç´æå ¡åã详ç»å»ºé è 模å¼å¯åé æ¤ç¯æç« ï¼ç£æ¬æµ®å¿«çº¿
äºãæ ¸å¿æ¥å£ä¸é ç½®åå¨æ¬è´¨
å¨.NET Coreä¸è¯»åé ç½®æ¯éè¿IConfigurationæ¥å£ï¼å®åå¨äºMicrosoft.Extensions.Configuration.Abstractions项ç®ä¸ï¼å¦ä¸å¾ï¼
IConfigurationï¼é 置访é®æ¥å£
IConfigurationProviderï¼é ç½®æä¾è æ¥å£
IConfigurationSourceï¼é ç½®æºæ¥å£
IConfigurationRootï¼é ç½®æ ¹æ¥å£ï¼ç»§æ¿IConfigurationï¼ç»´æ¤çIConfigurationProvideréååéæ°å è½½é ç½®
IConfigurationBuilderï¼IConfigurationRootæ¥å£å®ä¾çæé è æ¥å£
1.æå¡å®¹å¨ä¸IConfigurationå®ä¾æ³¨åï¼ConfigurationRootï¼
/// <summary>
/// Represents the root of an <see cref="IConfiguration"/> hierarchy. => é
ç½®æ ¹è·¯å¾
/// </summary>
public interface IConfigurationRoot : IConfiguration
{
/// <summary>
/// Force the configuration values to be reloaded from the underlying <see cref="IConfigurationProvider"/>s. => ä»é
ç½®æºéæ°å è½½é
ç½®
/// </summary>
void Reload();
/// <summary>
/// The <see cref="IConfigurationProvider"/>s for this configuration. => ä¾èµçé
ç½®æºéå
/// </summary>
IEnumerable<IConfigurationProvider> Providers { get; }
}
IConfigurationRootï¼ç»§æ¿IConfigurationï¼ç»´æ¤çä¸ä¸ªIConfigurationProvideréåå表ï¼ä¹å°±æ¯æ们çé ç½®æºãIConfigurationå®ä¾çå建并ééè¿new()æ¹å¼ï¼èæ¯ç±IConfigurationBuilderæ¥æ建ï¼å®ç°äºæéå è½½é ç½®æºï¼æ¯å»ºé è 模å¼çå åä½ç°ãIConfigurationBuilderä¸çæææä½å¦ï¼
HostBuilder.ConfigureAppConfiguration((context, builder) =>
{
builder.AddCommandLine(args); // å½ä»¤è¡é
ç½®æº
builder.AddEnvironmentVariables(); // ç¯å¢é
ç½®æº
builder.AddJsonFile("demo.json"); // jsonæ件é
ç½®æº
builder.AddInMemoryCollection(); // å
åé
ç½®æº
// ...
})
çæ¯ä¸ºIConfigurationRoot.Providersååå¤ï¼æåéè¿Build()æ¹æ³çæConfigurationRootå®ä¾æ³¨åå°æå¡å®¹å¨
public class HostBuilder : IHostBuilder
{
private HostBuilderContext _hostBuilderContext;
// é
ç½®æ建 å§æ
private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();
private IConfiguration _appConfiguration;
private void BuildAppConfiguration()
{
IConfigurationBuilder configBuilder = new ConfigurationBuilder();
foreach (Action<HostBuilderContext, IConfigurationBuilder> buildAction in _configureAppConfigActions)
{
buildAction(_hostBuilderContext, configBuilder);
}
_appConfiguration = configBuilder.Build(); // è°ç¨Build()å建IConfiguration å®ä¾ ConfigurationRoot
_hostBuilderContext.Configuration = _appConfiguration;
}
private void CreateServiceProvider()
{
var services = new ServiceCollection();
// register configuration as factory to make it dispose with the service provider
services.AddSingleton(_ => _appConfiguration); // 注å IConfiguration - åä¾
}
}
2.IConfiguration/IConfigurationSection读åé ç½®ä¸é ç½®å¨åæ¬è´¨
ç¨åºä¸æ们ä¼éè¿å¦ä¸æ¹å¼è·åé ç½®å¼ï¼å½ç¶è¿æç»å®IOptionsï¼
IConfiguration["key"]
IConfiguration.GetSection("key").Value
...
èIConfiguration注åçå®ä¾æ¯ConfigurationRootï¼ä»£ç å¦ä¸ï¼å ¶ç´¢å¼å¨å®ç°ç«æ¯ååºéåé ç½®æºï¼è·åé ç½®å¼ãåæ¥å½æ们éè¿IConfigurationè·åé ç½®æ¶ï¼å ¶å®å°±æ¯ååºéåIConfigurationBuilderå è½½è¿æ¥çé ç½®æºã
public class ConfigurationRoot : IConfigurationRoot, IDisposable
{
private readonly IList<IConfigurationProvider> _providers;
public IEnumerable<IConfigurationProvider> Providers => _providers;
public string this[string key]
{
get
{
// ååºéåé
ç½®æºï¼è·åå°é
ç½® å°±è¿åï¼è¿ä¹æ¯é
ç½®è¦ççæ ¹æ¬åå ï¼åæ·»å çç¸åé
ç½®ä¼è¦çåé¢ç
for (int i = _providers.Count - 1; i >= 0; i--)
{
IConfigurationProvider provider = _providers[i];
if (provider.TryGet(key, out string value))
{
return value;
}
}
return null;
}
}
}
é£ä¹é ç½®æ°æ®æ¯ä»¥ä»ä¹å½¢å¼åå¨çå¢ï¼å¨Microsoft.Extensions.Configuration项ç®ä¸ï¼æä¾äºä¸ä¸ªIConfigurationProvideré»è®¤å®ç°åå¨æ½è±¡ç±»ConfigurationProviderï¼é¨å代ç å¦ä¸
/// <summary>
/// Base helper class for implementing an <see cref="IConfigurationProvider"/>
/// </summary>
public abstract class ConfigurationProvider : IConfigurationProvider
{
protected ConfigurationProvider()
{
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
/// <summary>
/// The configuration key value pairs for this provider.
/// </summary>
protected IDictionary<string, string> Data { get; set; }
public virtual bool TryGet(string key, out string value)
=> Data.TryGetValue(key, out value);
/// <summary>
/// èæ¹æ³ï¼ä¾å
·ä½é
ç½®æºéåï¼å è½½é
ç½®å° Dataä¸
/// </summary>
public virtual void Load() { }
}
ä»ä¸å¯ç¥ï¼ææå è½½å°ç¨åºä¸çé ç½®æºï¼å ¶æ¬è´¨è¿æ¯åå¨å¨Provideréé¢ä¸ä¸ªç±»å为IDictionary<string, string> Dataå±æ§ä¸ãç±æ¤æ¨è®ºï¼ å½éè¿IConfigurationè·åé ç½®æ¶ï¼å°±æ¯éè¿å个ProviderçData读åï¼
ä¸ãç®æQueryStringé ç½®æºå®ç°
è¦å®ç°èªå®ä¹çé ç½®æºï¼åªéå®ç°IConfigurationProviderï¼IConfigurationSource两个æ¥å£å³å¯ï¼è¿ééè¿ä¸ä¸ªQueryStringæ ¼å¼çç®æé ç½®æ¥æ¼ç¤ºã
1.queryString.configæ°æ®æ ¼å¼å¦ä¸
server=localhost&port=3306&datasource=demo&user=root&password=123456&charset=utf8mb4
2.å®ç°IConfigurationSourceæ¥å£ï¼QueryStringConfiguationSourceï¼
public class QueryStringConfiguationSource : IConfigurationSource
{
public QueryStringConfiguationSource(string path)
{
Path = path;
}
/// <summary>
/// QueryStringæ件ç¸å¯¹è·¯å¾
/// </summary>
public string Path { get; }
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new QueryStringConfigurationProvider(this);
}
}
3.å®ç°IConfigurationProvideræ¥å£ï¼QueryStringConfiguationProviderï¼
public class QueryStringConfigurationProvider : ConfigurationProvider
{
public QueryStringConfigurationProvider(QueryStringConfiguationSource source)
{
Source = source;
}
public QueryStringConfiguationSource Source { get; }
/// <summary>
/// éåLoadæ¹æ³ï¼å°èªå®ä¹çé
置解æå° Data ä¸
/// </summary>
public override void Load()
{
// server=localhost&port=3306&datasource=demo&user=root&password=123456&charset=utf8mb4 ä¾åæ ¼å¼
string queryString = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, Source.Path));
string[] arrays = queryString.Split(new[] { "&" }, StringSplitOptions.RemoveEmptyEntries); // & å·åé
foreach (var item in arrays)
{
string[] temps = item.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries); // = å·åé
if (temps.Length != 2) continue;
Data.Add(temps[0], temps[1]);
}
}
}
4.IConfigurationBuilderé ç½®æºæ建
public static class QueryStringConfigurationExtensions
{
/// <summary>
/// é»è®¤æ件å称 queryString.config
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static IConfigurationBuilder AddQueryStringFile(this IConfigurationBuilder builder)
=> AddQueryStringFile(builder, "queryString.config");
public static IConfigurationBuilder AddQueryStringFile(this IConfigurationBuilder builder, string path)
=> builder.Add(new QueryStringConfiguationSource(path));
}
5.Programå è½½é ç½®æº
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(builder =>
{
// å è½½QueryStringé
ç½®æº
builder.AddQueryStringFile();
//builder.AddQueryStringFile("queryString.config");
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
è³æ¤ï¼èªå®ä¹QueryStringé ç½®æºå®ç°å®æï¼ä¾¿å¯éè¿IConfigurationæ¥å£è·åå¼ï¼ç»æå¦ä¸
IConfiguration["server"] => localhost
IConfiguration["datasource"] => demo
IConfiguration["charset"] => utf8mb4
...
åã宿主é ç½®ä¸åºç¨é ç½®
.NET Coreå®æ¹å·²é»è®¤æä¾äºï¼ç¯å¢åéãå½ä»¤è¡åæ°ï¼JsonãIniçé ç½®æºï¼ä¸è¿éç¨åºæ¯å´åºæä¸åãä¸å¦¨å¯å为两类ï¼ä¸ç±»æ¯å®¿ä¸»é ç½®æºï¼ä¸ç±»æ¯åºç¨é ç½®æº
1.宿主é ç½®æº
宿主é ç½®æºï¼ä¾IHost宿主å¯å¨æ¶ä½¿ç¨çé ç½®æºãç¯å¢åéãå½ä»¤è¡åæ°å°±å¯å½ä¸ºè¿ç±»ï¼ä»¥IHostEnvironment为ä¾
/// <summary>
/// æä¾è¿è¡ç¯å¢ç¸å
³ä¿¡æ¯
/// </summary>
public interface IHostEnvironment
{
string EnvironmentName { get; set; }
string ApplicationName { get; set; }
string ContentRootPath { get; set; }
}
IHostEnvironmentæ¥å£æä¾äºå½ååºç¨è¿è¡ç¯å¢ç¸å ³ä¿¡æ¯ï¼å¯ä»¥éè¿IsEnvironment()æ¹æ³å¤æå½åè¿è¡ç¯å¢æ¯Developmentè¿æ¯ProductionãStagingã
public static bool IsEnvironment(this IHostEnvironment hostEnvironment, string environmentName)
{
if (hostEnvironment == null)
{
throw new ArgumentNullException(nameof(hostEnvironment));
}
return string.Equals(hostEnvironment.EnvironmentName, environmentName, StringComparison.OrdinalIgnoreCase);
}
hostEnvironment.EnvironmentNameæ¯ä»ä¹ï¼è¿å°±å¾çäºå®æ³¨åå°æå¡å®¹å¨æ¶æèµçå¼ï¼HostBuilder
public class HostBuilder:IHostBuilder
{
private void CreateHostingEnvironment()
{
_hostingEnvironment = new HostingEnvironment()
{
ApplicationName = _hostConfiguration[HostDefaults.ApplicationKey], // _hostConfiguration ç±»åæ¯ IConfiguration
EnvironmentName = _hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production, // å½æªé
ç½®ç¯å¢æ¶ï¼é»è®¤Productionç¯å¢ï¼å¨ä½¿ç¨vså¼åå¯å¨æ¶ï¼lanuchSetting.json é
ç½®äº ç¯å¢åéï¼"ASPNETCORE_ENVIRONMENT": "Development"
ContentRootPath = ResolveContentRootPath(_hostConfiguration[HostDefaults.ContentRootKey], AppContext.BaseDirectory),
};
if (string.IsNullOrEmpty(_hostingEnvironment.ApplicationName))
{
// Note GetEntryAssembly returns null for the net4x console test runner.
_hostingEnvironment.ApplicationName = Assembly.GetEntryAssembly()?.GetName().Name;
}
}
}
ç±æ¤å¯è§ï¼IHostEnvironmentææä¾çä¿¡æ¯æ ¹ç±ä»æ¯ä»IConfiguration读åï¼èè¿äºé ç½®æ£æ¯æ¥èªç¯å¢åéãå½ä»¤è¡åæ°é ç½®æºã
2.åºç¨é ç½®æº
åºç¨é ç½®æºï¼ä¾åºç¨ä¸å¡é»è¾ä½¿ç¨çé ç½®æºãJsonãIniãXml以åèªå®ä¹çQueryStringçå°±å¯å½ä¸ºç±»ã
æ件é ç½®æºé ç½®æ´æ°åç
对äºæ件é ç½®æºï¼.NET Coreé»è®¤æä¾äºä¸¤ä¸ªæ½è±¡ç±»ï¼FileConfigurationSource å FileConfigurationProvider
public abstract class FileConfigurationProvider : ConfigurationProvider, IDisposable
{
private readonly IDisposable _changeTokenRegistration;
public FileConfigurationProvider(FileConfigurationSource source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
Source = source;
if (Source.ReloadOnChange && Source.FileProvider != null)
{
_changeTokenRegistration = ChangeToken.OnChange( // æ件æ¹åï¼éæ°å è½½é
ç½®
() => Source.FileProvider.Watch(Source.Path),
() =>
{
Thread.Sleep(Source.ReloadDelay);
Load(reload: true);
});
}
}
/// <summary>
/// The source settings for this provider.
/// </summary>
public FileConfigurationSource Source { get; }
private void Load(bool reload)
{
IFileInfo file = Source.FileProvider?.GetFileInfo(Source.Path);
if (file == null || !file.Exists)
{
if (Source.Optional || reload) // Always optional on reload
{
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // Data 被éæ°å建æ°çå®ä¾èµå¼äº
}
else
{
var error = new StringBuilder($"The configuration file '{Source.Path}' was not found and is not optional.");
if (!string.IsNullOrEmpty(file?.PhysicalPath))
{
error.Append($" The physical path is '{file.PhysicalPath}'.");
}
HandleException(ExceptionDispatchInfo.Capture(new FileNotFoundException(error.ToString())));
}
}
else
{
// Always create new Data on reload to drop old keys
if (reload)
{
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // Data 被éæ°å建æ°çå®ä¾èµå¼äº
}
static Stream OpenRead(IFileInfo fileInfo)
{
if (fileInfo.PhysicalPath != null)
{
// The default physical file info assumes asynchronous IO which results in unnecessary overhead
// especally since the configuration system is synchronous. This uses the same settings
// and disables async IO.
return new FileStream(
fileInfo.PhysicalPath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite,
bufferSize: 1,
FileOptions.SequentialScan);
}
return fileInfo.CreateReadStream();
}
using Stream stream = OpenRead(file);
try
{
Load(stream);
}
catch (Exception e)
{
HandleException(ExceptionDispatchInfo.Capture(e));
}
}
}
public override void Load()
{
Load(reload: false);
}
public abstract void Load(Stream stream);
}
ææåºäºæ件é ç½®æºï¼å¦æè¦çæ§é ç½®æ件æ´æ°ï¼å¦ï¼appsetting.jsonï¼é½åºå®ç°è¿ä¸ªä¸¤ä¸ªæ½è±¡ç±»ï¼å°½ç®¡ä¸æChangeTokenæ¯ä¸ªä»ä¹ä¸ä¸ï¼åªéæç½Provider.Data å¨æ件åæ´æ¶è¢«éæ°èµå¼ä¹æªå°ä¸å¯ã