標籤:選擇 strong .config 擷取 設計 none data 變化 cond
轉自https://www.cnblogs.com/tonnie/archive/2010/12/17/appconfig.html
在一般的項目中,為了使你的代碼更加靈活,更方便調整,減少不必要的hard code,我們都在config中添加許多配置資訊,一般可以選擇.NET內建的設定檔形式app.config或者web項目中的web.config來完成配置工作。.NET中提供了幾個和配置有關的類來支援用完輕鬆的完成設定檔的讀寫設定:
System.Configuration.ConfigurationSectionGroup一般和你項目中使用的Assambly保持1:1的對應關係,這樣劃分使得結構相對清晰,權責明確。當然你可以不使用它,這樣一旦你的Assambly在別的地方要被重用時,找出相應的config資訊就變得很困難。
System.Configuration.ConfigurationSection維護一個相對獨立的配置節,使用時需現在<ConfigSections></ConfigSections>節點下聲明。我們熟悉的<appSettings></appSettings>以及<connectionStrings></connectionStrings/>就是.NET為我們預留的一個Section。
System.Configuration.ConfigurationElementCollection & System.Configuration.ConfigurationElement就是Section下具體的配置資訊和配置資訊的集合了。 下面來看看怎麼使用這些類玩轉app.config 1.初級玩法 最初級的用法當然是使用<appSettings/>,我們在app.config 中添加
<configuration> <appSettings> <add key="MyConfigString" value="Test Config Data"/> </appSettings></configuration>
訪問它
public class AppSettingConfig { public string resultValue; public AppSettingConfig() { this.resultValue = ConfigurationManager.AppSettings["MyConfigString"].ToString(); } } [TestMethod] public void TestAppSettingConfigNode() { AppSettingConfig appCon = new AppSettingConfig(); Assert.AreEqual("Test Config Data", appCon.resultValue); }沒有問題!
我們加個Section來看看如何訪問:
<configuration> <configSections> <sectionGroup name="MySectionGroup"> <section name="MyFirstSection" type="System.Configuration.DictionarySectionHandler"/> <section name="MySecondSection" type="System.Configuration.DictionarySectionHandler"/> </sectionGroup> </configSections> <MySectionGroup> <MyFirstSection> <add key="First" value="First Section"/> </MyFirstSection> <MySecondSection> <add key="Second" value="Second Section"/> </MySecondSection> </MySectionGroup></configuration>
注意我們在section的type中給出了System.Configuration.DictionarySectionHandler,這也限制了我們在具體的ConfigurationElement中只能使用<add key=”” value=””/>的形式,使得我們GetSection()方法返回的是一個IDictory對象,我們可以根據Key來取得相應的值
public class SectionConfig { public string resultValue; public SectionConfig() { System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); IDictionary dic = ConfigurationManager.GetSection("MySectionGroup/MySecondSection") as IDictionary; this.resultValue = dic["Second"].ToString(); } }
[TestMethod] public void TestSectionGroupConfigNode() { SectionConfig sc = new SectionConfig(); Assert.AreEqual("First Section", sc.resultValue); }
還是沒問題。
2. 中級玩法
.NET支援對上述提到的configuration類進行擴充,我們可以定義自己的Section。
繼承自基類System.Configuration.ConfigurationSection,ConfigurationSection已經提供了索引器用來擷取設定資料。
在類中加上ConfigurationProperty屬性來定義Section中的Element:
public class CustomSection:System.Configuration.ConfigurationSection { [ConfigurationProperty("sectionId", IsRequired=true, IsKey=true)] public int SectionId { get { return (int)base["sectionId"]; } set { base["sectionId"] = value; } } [ConfigurationProperty("sectionValue", IsRequired = false)] public string SectionValue { get { return base["sectionValue"].ToString(); } set { base["sectionValue"] = value; } } }
操作此Section,我們將其動態加入app.config中,並讀出來:
public class CustomSectionBroker { private CustomSection customSection = null; public void InsertCustomSection() { customSection = new CustomSection(); customSection.SectionId = 1; customSection.SectionValue = "The First Value"; System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); config.Sections.Add("CustomSection", customSection); config.Save(ConfigurationSaveMode.Minimal); } public int GetCustomSectionID() { System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); CustomSection cs = config.GetSection("CustomSection") as CustomSection; return cs.SectionId; } } [TestMethod] public void TestCustomSection() { CustomSectionBroker cb = new CustomSectionBroker(); cb.InsertCustomSection(); Assert.AreEqual(1, cb.GetCustomSectionID()); }
可以看下現在app.config檔案的變化:
<configuration> <configSections> <section name="CustomSection" type="Tonnie.Configuration.Library.CustomSection, Tonnie.Configuration.Library, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <sectionGroup name="MySectionGroup"> <section name="MyFirstSection" type="System.Configuration.DictionarySectionHandler"/> <section name="MySecondSection" type="System.Configuration.DictionarySectionHandler"/> </sectionGroup> </configSections> <CustomSection sectionId="1" sectionValue="The First Value" /> <MySectionGroup> <MyFirstSection> <add key="First" value="First Section"/> </MyFirstSection> <MySecondSection> <add key="Second" value="Second Section"/> </MySecondSection> </MySectionGroup></configuration>
增加了一個單獨的Section,名為"CustomSection",並且包含了我們建立的2個configurationProperty。我們還可以繼續作擴充,現在我們的config中section的部分呈現的是<CustomSection sectionId="1" sectionValue="The First Value" /> ,這樣對於複雜的配置資訊仍然不方便,我們是不是可以繼續擴充,將其變成比較合理的<CustomSection><ChildCustomSectionA childId=1 childValue=”ChildA”></ChildCustomSectionA><ChildCustomSectionB childid=2 childValue=”ChildB”></ChildCustomSectionB></CustomSection>這種方式呢? 我們為<ChildCustomSectionA></ChildCustomSectionA>建立擴充自ConfigurationElement類的子類CustomSectionElementA,然後修改CustomSection類中的Property,使得類型不再是int 或 string,而是我們建立的新類CustomSectionElementA.由於ChildCustomSectionA 和ChildCustomSectionB 的結構相對一致,根據物件導向的開發封閉原則,我們可以先抽象出一個base類,然後讓ChildCustomSectionA,ChildCustomSectionB分別繼承自此base類,當以後要添加更多的ChildCustomSectionC,ChildCustomSectionD…時,使用這種Template的設計模式,將更加靈活。
public abstract class CustomSectionElementBase:System.Configuration.ConfigurationElement { [ConfigurationProperty("childId", IsRequired=true, IsKey=true)] public int ChildID { get{return (int)base["childId"];} set{base["childId"] = value;} } [ConfigurationProperty("childValue", IsRequired=true)] public string ChildValue { get{return base["childValue"].ToString();} set{base["childValue"] = value;} } } public class CustomSectionElementA:CustomSectionElementBase { public CustomSectionElementA() { base.ChildID = 1; base.ChildValue = "ChildA"; } } public class CustomSectionElementB:CustomSectionElementBase { public CustomSectionElementB() { base.ChildID = 2; base.ChildValue = "ChildB"; } }
完成了ConfigurationElement的實現,我們可以改寫我們上一個例子中定義的CustomSection類了:
public class CustomSectionWithChildElement:System.Configuration.ConfigurationSection { private const string elementChildA = "childSectionA"; private const string elementChildB = "childSectionB"; [ConfigurationProperty(elementChildA, IsRequired=true, IsKey=true)] public CustomSectionElementA ChildSectionA { get { return base[elementChildA] as CustomSectionElementA; } set { base[elementChildA] = value; } } [ConfigurationProperty(elementChildB, IsRequired = true)] public CustomSectionElementB ChildSectionB { get { return base[elementChildB] as CustomSectionElementB; } set { base[elementChildB] = value; } } } public class CustomSectionWithChildElementBroker { private CustomSectionWithChildElement customSection = null; public void InsertCustomSection() { customSection = new CustomSectionWithChildElement(); customSection.ChildSectionA = new CustomSectionElementA(); customSection.ChildSectionB= new CustomSectionElementB(); System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); config.Sections.Add("CustomSectionWithChildElement", customSection); config.Save(ConfigurationSaveMode.Minimal); } public int GetCustomSectionChildAID() { System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); CustomSectionWithChildElement cswe = config.GetSection("CustomSectionWithChildElement") as CustomSectionWithChildElement; return cswe.ChildSectionA.ChildID; } }
紅色字型就是修改的地方了,將Property改成我們自訂類的形式.測試代碼如下:
[TestMethod] public void TestCustomSectionWithChildElement() { CustomSectionWithChildElementBroker cweb = new CustomSectionWithChildElementBroker(); cweb.InsertCustomSection(); Assert.AreEqual(1, cweb.GetCustomSectionChildAID()); }
看看運行後我們的app.config變成什麼樣子了:
<configuration> <configSections> <section name="CustomSectionWithChildElement" type="Tonnie.Configuration.Library.CustomSectionWithChildElement, Tonnie.Configuration.Library, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <section name="CustomSection" type="Tonnie.Configuration.Library.CustomSection, Tonnie.Configuration.Library, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <sectionGroup name="MySectionGroup"> <section name="MyFirstSection" type="System.Configuration.DictionarySectionHandler"/> <section name="MySecondSection" type="System.Configuration.DictionarySectionHandler"/> </sectionGroup> </configSections> <CustomSectionWithChildElement> <childSectionA childId="1" childValue="ChildA" /> <childSectionB childId="2" childValue="ChildB" /> </CustomSectionWithChildElement> <CustomSection sectionId="1" sectionValue="The First Value" /> <MySectionGroup> <MyFirstSection> <add key="First" value="First Section"/> </MyFirstSection> <MySecondSection> <add key="Second" value="Second Section"/> </MySecondSection> </MySectionGroup></configuration>
cool,好像完成了我們的要求。
下面為我們的CustomSectionWithChildElement外面再加一層SectionGroup.
public class CustomSectionGroup : System.Configuration.ConfigurationSectionGroup { [ConfigurationProperty("customSectionA", IsRequired = true, IsKey = true)] public CustomSectionWithChildElement SectionA { get { return base.Sections["customSectionA"] as CustomSectionWithChildElement; } set { this.Sections.Add("customSectionA", value); } } }
public class CustomSectionGroupWithChildElementBroker { private CustomSectionWithChildElement customSection = null; public void InsertCustomSectionGroup() { customSection = new CustomSectionWithChildElement(); customSection.ChildSectionA = new CustomSectionElementA(); customSection.ChildSectionB= new CustomSectionElementB(); CustomSectionGroup sectionGroup = new CustomSectionGroup(); System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); if (config.GetSectionGroup("customSectionGroup") == null) config.SectionGroups.Add("customSectionGroup",sectionGroup); sectionGroup.SectionA = customSection; config.Save(ConfigurationSaveMode.Minimal); } public int GetCustomSectionChildAID() { System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); CustomSectionWithChildElement cswe = config.GetSection("customSectionGroup/customSectionA") as CustomSectionWithChildElement; return cswe.ChildSectionA.ChildID; } }
測試一下:
[TestMethod] public void TestCustomSectionGroupWithChildElement() { CustomSectionGroupWithChildElementBroker cweb = new CustomSectionGroupWithChildElementBroker(); cweb.InsertCustomSectionGroup(); Assert.AreEqual(1, cweb.GetCustomSectionChildAID()); }沒問題,看下現在的app.config,是不是更加結構化了:
<configuration> <configSections> <sectionGroup name="MySectionGroup"> <section name="MyFirstSection" type="System.Configuration.DictionarySectionHandler"/> <section name="MySecondSection" type="System.Configuration.DictionarySectionHandler"/> </sectionGroup> <sectionGroup name="customSectionGroup" type="Tonnie.Configuration.Library.CustomSectionGroup, Tonnie.Configuration.Library, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" > <section name="customSectionA" type="Tonnie.Configuration.Library.CustomSectionWithChildElement, Tonnie.Configuration.Library, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </sectionGroup> </configSections> <MySectionGroup> <MyFirstSection> <add key="First" value="First Section"/> </MyFirstSection> <MySecondSection> <add key="Second" value="Second Section"/> </MySecondSection> </MySectionGroup> <customSectionGroup> <customSectionA> <childSectionA childId="1" childValue="ChildA" /> <childSectionB childId="2" childValue="ChildB" /> </customSectionA> </customSectionGroup></configuration>
3 進階玩法
到目前為止可能大家對app.config有了一定的認識了,我們自己可以不斷的去擴充.NET Framework提供給我們的類,從SectionGroup,Section,ElementCollection,Element 從上自下的一級一級的組裝成符合工程化項目設定檔需要的形式。當遇到可能配置元素的類型屬性差不多時,可以抽象出一個base類來。比如可以抽象出Section這一層面的base類,或者ElementCollection,Element這一層的抽象類別(可以是抽象的泛型類)來。同時增加泛型來更好的支援擴充。具體例子下次再給了。
附上所有代碼:/Files/tonnie/Tonnie.Configuration.rar
一點點心得,歡迎交流……
一步一步教你玩轉.NET Framework的設定檔app.config