主頁 > .NET開發 > 淺析Asp.Net Core框架IConfiguration配置

淺析Asp.Net Core框架IConfiguration配置

2021-01-28 06:09:13 .NET開發

目錄

  • 一、建造者模式(Builder Pattern)
  • 二、核心介面與配置存盤本質
  • 三、簡易QueryString配置源實作
  • 四、宿主配置與應用配置
  • 五、檔案配置源配置更新原理

一、建造者模式

為什么提建造者模式?在閱讀.NET Core原始碼時,時常碰到IHostBuilder,IConfigurationBuilder,ILoggerBuilder等諸如此類帶Builder名稱的類/介面,起初專研時那是一頭愣,知識不夠,勤奮來湊,在了解到Builder模式后終于理解,明白這些Builder類是用來構建相對應類的物件,用完即毀別無他用,理解建造者模式,有助于閱讀原始碼時發現核心介面/類,將檔案分類,直指堡壘,詳細建造者模式可參閱此篇文章:磁懸浮快線

二、核心介面與配置存盤本質

在.NET Core中讀取配置是通過IConfiguration介面,它存在于Microsoft.Extensions.Configuration.Abstractions專案中,如下圖:
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 = https://www.cnblogs.com/GodX/archive/2021/01/27/new Dictionary(StringComparer.OrdinalIgnoreCase);
	}

	/// 
	/// The configuration key value pairs for this provider.
	/// 
	protected IDictionary Data { get; set; }

	public virtual bool TryGet(string key, out string value)
		=> Data.TryGetValue(key, out value);
	/// 
	/// 虛方法,供具體配置源重寫,加載配置到 Data中
	/// 
	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 = https://www.cnblogs.com/GodX/archive/2021/01/27/new Dictionary(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 = https://www.cnblogs.com/GodX/archive/2021/01/27/new Dictionary(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 在檔案變更時被重新賦值也未嘗不可,

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/253321.html

標籤:.NET技术

上一篇:從零搭建一個IdentityServer——專案搭建

下一篇:基于 Mediasoup 的 Abp vNext 視頻會議模塊

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more