在上一篇中,限量提到的.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
留言
張貼留言