在上一篇中,限量提到的.Net Configuration System的基本概念與基本使用,接著這篇Part II 要來看看更複雜的Configuration File結構,巢狀與陣列。
上面範例的例子為一個公司的系統設定,第一層的Setting有Name, Type與巢狀的Cache屬性,意思為:設定檔名稱為DebugSetting,系統為Console類型。裏頭使用Cache機制;第二層Cache有Name, Path與巢狀的Global和Session屬性。意思是使用的Cache類別是FileCache,在LimitedLib.Framework.Cache DLL裡,然後FileCache有Global與Session的設定值;再來針對FileCache的Global與Session有不同的設定,Global定義了Cache 30秒,Session定義Cache 60秒後會自動失效。
以此方式我們就可以一層層的加入巢狀設定,讓設定架構更佳完整,再來我們來看看如何使用.Net Configuration System的程式碼:
SettingSection.cs
CacheEle.cs
CacheGlobalEle.cs
CacheSessionEle.cs
Program.cs
既然設定檔改了,那程式碼也要調整一下,基本上要調整的只有 SettingSection.cs,另外還要加入CacheEleCollection.cs:
SettingSection.cs
CacheEleCollection.cs
在SettingSection.cs中,限量將Cache Property改成Caches,回傳CacheEleCollection,這裡要注意的是要額外加上ConfigurationCollection Attribute,在ConfigurationCollection Attribute要指定陣列的資料型別與子項目的名稱(設定AddItemName,因為預設子項目名稱為add,另外還有remove, clear,詳細請見Configuration Sections Schema)。
在CacheEleCollection.cs中,要實作覆寫兩個方法,另外,限量加上了Index,這樣方便用Index取得特定位置的子項目,或者可以考慮實作用Key的Index去取得子項目。
再來看看執行的結果:
了解這兩個結構類型後就可以做隨意變化,像是巢狀陣列...,愈複雜的結構只會產生出更多程式碼,所以下一篇限量就要來做簡化程式碼的動作,請期待下集。
站內相關文章:
C# - App.config自訂section程式碼架構Part I(基本用法)
C# - App.config自訂section程式碼架構Part III (使用泛型)
參考來源:
MSDN - Configuration Sections Schema
巢狀(Nested)
巢狀就是類似的資料結構,層層相疊,就像俄羅斯娃娃一樣,可一層一層剝開。每一層有各自的屬性,而下一層的設定可為這一層的某個屬性,以下為三層式的巢狀結構:<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="Company"> <section name="Setting" type="TestSolution.TestSection, TestSolution" /> </sectionGroup> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <Company> <Setting name="DebugSetting" type="Console"> <Cache name="FileCache" path="LimitedLib.Framework.Cache"> <Global duration="30000"/> <Session duration="60000" autoExpire="true"/> </Cache> </Setting> </Company> </configuration>
上面範例的例子為一個公司的系統設定,第一層的Setting有Name, Type與巢狀的Cache屬性,意思為:設定檔名稱為DebugSetting,系統為Console類型。裏頭使用Cache機制;第二層Cache有Name, Path與巢狀的Global和Session屬性。意思是使用的Cache類別是FileCache,在LimitedLib.Framework.Cache DLL裡,然後FileCache有Global與Session的設定值;再來針對FileCache的Global與Session有不同的設定,Global定義了Cache 30秒,Session定義Cache 60秒後會自動失效。
以此方式我們就可以一層層的加入巢狀設定,讓設定架構更佳完整,再來我們來看看如何使用.Net Configuration System的程式碼:
SettingSection.cs
public class SettingSection : ConfigurationSection { [ConfigurationProperty("name", IsRequired = true)] public string Name { get { return this["name"] as string; } set { this["name"] = value; } } [ConfigurationProperty("type", IsRequired = false)] public string Type { get { return this["type"] as string; } set { this["type"] = value; } } [ConfigurationProperty("Cache")] public CacheEle Cache { get { return this["Cache"] as CacheEle; } } }
CacheEle.cs
public class CacheEle : ConfigurationElement { [ConfigurationProperty("name", IsRequired = true)] public string Name { get { return this["name"] as string; } set { this["name"] = value; } } [ConfigurationProperty("path", IsRequired = true)] public string Path { get { return this["path"] as string; } set { this["path"] = value; } } [ConfigurationProperty("Global")] public CacheGlobalEle Global { get { return this["Global"] as CacheGlobalEle; } } [ConfigurationProperty("Session")] public CacheSessionEle Session { get { return this["Session"] as CacheSessionEle; } } }
CacheGlobalEle.cs
public class CacheGlobalEle : ConfigurationElement { [ConfigurationProperty("duration")] public string Duration { get { return this["duration"] as string; } set { this["duration"] = value; } } }
CacheSessionEle.cs
public class CacheSessionEle : ConfigurationElement { [ConfigurationProperty("duration")] public string Duration { get { return this["duration"] as string; } set { this["duration"] = value; } } [ConfigurationProperty("autoExpire")] public string AutoExpire { get { return this["autoExpire"] as string; } set { this["autoExpire"] = value; } } }因為每個不同的巢狀設定就要建立一個類別去Mapping,所以當設定結構愈複雜的時候,程式碼的量就會愈多,這個部分限量會在Part III中去說明如何減少程式碼的量。先不多說了,我們來寫個遞迴程式來印出所有的巢狀設定:
Program.cs
class Program { static void Main(string[] args) { var section = ConfigurationManager.GetSection("Company/Setting") as SettingSection; OutputInfo(string.Empty, section); /* * 執行結果: * SettingSection - * Name - DebugSetting * Type - Console * CacheEle - * Name - FileCache * Path - LimitedLib.Framework.Cache * CacheGlobalEle - * Duration - 30000 * CacheSessionEle - * Duration - 60000 * AutoExpire - true */ Console.Read(); } /// <summary> /// 遞迴印出每一層設定值 /// </summary> static void OutputInfo(string prefix, ConfigurationElement ele) { OutputLine($"{prefix}{ele.GetType().Name} -"); prefix += "\t"; foreach (var prop in ele.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { OutputLine($"{prefix}{prop.Name} - {prop.GetValue(ele)}"); // 檢查屬性宣告的DataType是否繼承ConfigurationElement與是否來自目前執行的Assembly中 if (prop.PropertyType.IsSubclassOf(typeof(ConfigurationElement)) && (prop.PropertyType.Assembly == Assembly.GetExecutingAssembly())) { OutputInfo(prefix, prop.GetValue(ele) as ConfigurationElement); } } } static void OutputLine(string str) { Console.WriteLine(str); Debug.WriteLine(str); } }
陣列(Array)
在任何資料結構中,陣列是一個很重要且常出現的資料結構,在這裡也是一樣的。同一個設定結構,可能會同時出現或使用,以上面那個系統設定檔來說,FileCache, DBCache, MemoryCache...等各種Cache機制是可以同時存在的,所以下面我們就來看看使用陣列結構的設定檔長成怎樣,這裡限量用前一個範例做變化:<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="Company"> <section name="Setting" type="TestSolution.SettingSection, TestSolution" /> </sectionGroup> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <Company> <Setting name="DebugSetting" type="Console"> <Caches> <Cache name="FileCache" path="LimitedLib.Framework.Cache"> <Global duration="30000"/> <Session duration="60000" autoExpire="true"/> </Cache> <Cache name="DBCache" path="LimitedLib.Plugin.Cache"> <Global duration="10000"/> </Cache> <Cache name="MemoryCache" path="LimitedLib.Framework.Cache"> <Session duration="10000" autoExpire="false"/> </Cache> </Caches> </Setting> </Company> </configuration>這個範例和前一個一樣,只是限量在這裡的Cache加入了DBCache和MemoryCache的設定,這些Cache的設定值外層由Caches包裝。
既然設定檔改了,那程式碼也要調整一下,基本上要調整的只有 SettingSection.cs,另外還要加入CacheEleCollection.cs:
SettingSection.cs
public class SettingSection : ConfigurationSection { [ConfigurationProperty("name", IsRequired = true)] public string Name { get { return this["name"] as string; } set { this["name"] = value; } } [ConfigurationProperty("type", IsRequired = false)] public string Type { get { return this["type"] as string; } set { this["type"] = value; } } [ConfigurationProperty("Caches")] [ConfigurationCollection(typeof(CacheEle), AddItemName = "Cache")] public CacheEleCollection Caches { get { return this["Caches"] as CacheEleCollection; } } }
CacheEleCollection.cs
public class CacheEleCollection : ConfigurationElementCollection { protected override ConfigurationElement CreateNewElement() { return new CacheEle(); } protected override object GetElementKey(ConfigurationElement element) { return (element as CacheEle).Name; } public CacheEle this[int index] { get { return this.BaseGet(index) as CacheEle; } } }
在SettingSection.cs中,限量將Cache Property改成Caches,回傳CacheEleCollection,這裡要注意的是要額外加上ConfigurationCollection Attribute,在ConfigurationCollection Attribute要指定陣列的資料型別與子項目的名稱(設定AddItemName,因為預設子項目名稱為add,另外還有remove, clear,詳細請見Configuration Sections Schema)。
在CacheEleCollection.cs中,要實作覆寫兩個方法,另外,限量加上了Index,這樣方便用Index取得特定位置的子項目,或者可以考慮實作用Key的Index去取得子項目。
再來看看執行的結果:
class Program { static void Main(string[] args) { var section = ConfigurationManager.GetSection("Company/Setting") as SettingSection; OutputInfo(string.Empty, section); /* * 執行結果: * SettingSection - * Name - DebugSetting * Type - Console * Caches - TestSolution.CacheEleCollection * CacheEle - * Name - FileCache * Path - LimitedLib.Framework.Cache * Global - TestSolution.CacheGlobalEle * CacheGlobalEle - * Duration - 30000 * Session - TestSolution.CacheSessionEle * CacheSessionEle - * Duration - 60000 * AutoExpire - true * CacheEle - * Name - DBCache * Path - LimitedLib.Plugin.Cache * Global - TestSolution.CacheGlobalEle * CacheGlobalEle - * Duration - 10000 * Session - TestSolution.CacheSessionEle * CacheSessionEle - * Duration - * AutoExpire - * CacheEle - * Name - MemoryCache * Path - LimitedLib.Framework.Cache * Global - TestSolution.CacheGlobalEle * CacheGlobalEle - * Duration - * Session - TestSolution.CacheSessionEle * CacheSessionEle - * Duration - 10000 * AutoExpire - false */ Console.Read(); } /// <summary> /// 遞迴印出每一層設定值 /// </summary> static void OutputInfo(string prefix, ConfigurationElement ele) { OutputLine($"{prefix}{ele.GetType().Name} -"); prefix += "\t"; foreach (var prop in ele.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { OutputLine($"{prefix}{prop.Name} - {prop.GetValue(ele)}"); // 檢查屬性宣告的DataType是否繼承ConfigurationElement與是否來自目前執行的Assembly中 if (prop.PropertyType.IsSubclassOf(typeof(ConfigurationElement)) && (prop.PropertyType.Assembly == Assembly.GetExecutingAssembly())) { // 是否為ConfigurationElementCollection if (prop.PropertyType.IsSubclassOf(typeof(ConfigurationElementCollection))) { foreach(var item in prop.GetValue(ele) as ConfigurationElementCollection) { OutputInfo(prefix + "\t", item as ConfigurationElement); continue; } } else { OutputInfo(prefix, prop.GetValue(ele) as ConfigurationElement); continue; } } } } static void OutputLine(string str) { Console.WriteLine(str); Debug.WriteLine(str); } }
了解這兩個結構類型後就可以做隨意變化,像是巢狀陣列...,愈複雜的結構只會產生出更多程式碼,所以下一篇限量就要來做簡化程式碼的動作,請期待下集。
站內相關文章:
C# - App.config自訂section程式碼架構Part I(基本用法)
C# - App.config自訂section程式碼架構Part III (使用泛型)
參考來源:
MSDN - Configuration Sections Schema
留言
張貼留言