前言
昨天修改代碼發現了一個問題,由於自己要在WCF服務接口中添加了一個方法,那么在相應調用的地方進行更新服務就可以了,不料意外發生了,竟然無法更新。左查右查終於發現了問題。App.config配置文件中的配置貌似出現了問題。查找節點發現是如下節點:
<configSections>
<section name="Test1" type="Demo.Section1,Demo"/>
..............
</configSections>
我當時也只是看到了下划波浪線,才猜測是這里的問題,於是我把configSections節點注釋后,重新更新WCF服務,沒想到真的可以更新了,心想這是個什么節點呢,雖然之前自己也見過這個節點,但是從來沒用過,於是趁此機會就進行簡單的學習一下吧,以便之后說不定什么時候就會用到。
這里我的講解暫時之針對.NET的Web.config文件和App.confg文件,也就是對.Net配置文件自定義節點進行學習記錄。
配置文件優先級
在此簡單的學習一個配置文件的優先級吧,因為自己之前對配置文件接觸的也比較少,沒詳細的進行學習過。
首先在.net提供了一個針對當前機器的配置文件,這個文件是machine.config。所在地址如下圖所示。

然后此文件夾下還存在一個Web.confg的配置文件。
asp.net網站IIS啟動的時候會加載配置文件中的配置信息,然后緩存這些信息,這樣就不必每次去讀取配置信息。在運行過程中asp.net應用程序會監視配置文件的變化情況,一旦編輯了這些配置信息,就會重新讀取這些配置信息並緩存。
1、如果在當前頁面所在目錄下存在web.config文件,查看是否存在所要查找的結點名稱,如果存在返回結果並停止查找。
2、如果當前頁面所在目錄下不存在web.config文件或者web.config文件中不存在該結點名,則查找它的上級目錄,直到網站的根目錄。
3、如果網站根目錄下不存在web.config文件或者web.config文件中不存在該節點名則在C:WindowsMicrosoft.NETFrameworkv4.0.30319Config/web.config文件中查找。(這是我本機的地址,請根據情況進行調整)
4、如果在C:WindowsMicrosoft.NETFrameworkv4.0.30319Config/web.config文件中不存在相應結點,則在C:WindowsMicrosoft.NETFrameworkv4.0.30319Config/machine.config文件中查找。
5、如果仍然沒有找到則返回null。
所以如果我們對某個網站或者某個文件夾有特定要求的配置,可以在相應的文件夾下創建一個web.config文件,覆蓋掉上級文件夾中的web.config文件中的同名配置即可。這些配置信息的尋找只查找一次,以后便被緩存起來供后來的調用。在asp.net應用程序運行過程中,如果web.config文件發生更改就會導致相應的應用程序重新啟動,這時存儲在服務器內存中的用戶會話信息就會丟失(如存儲在內存中的Session)。
所以如果我們對某個網站或者某個文件夾有特定要求的配置,可以在相應的文件夾下創建一個web.config文件,覆蓋掉上級文件夾中的web.config文件中的同名配置即可。這些配置信息的尋找只查找一次,以后便被緩存起來供后來的調用。在asp.net應用程序運行過程中,如果web.config文件發生更改就會導致相應的應用程序重新啟動,這時存儲在服務器內存中的用戶會話信息就會丟失(如存儲在內存中的Session)。一些軟件(如殺毒軟件)每次完成對web.config的訪問時就會修改web.config的訪問時間屬性,也會導致asp.net應用程序的重啟。
常用配置文件節點appSettings和connectionSettings說明
1、<appSettings>節點
<appSettings>節點主要用來存儲asp.net應用程序的配置信息,例如網站上傳文件的類型:
<appSettings>
<!--允許上傳的格式類型-->
<add key="ImageType" value=".jpg;.bmp;.gif;.png;.jpeg"/>
<!--允許上傳的文件類型-->
<add key="FileType" value=".jpg;.bmp;.gif;.png;.jpeg;.pdf;.zip;.rar;.xls;.doc"/>
</appSettings>
對於<appSettings>節點中的值可以按照key來進行訪問,以下就是一個讀取key值為“FileType”節點值的例子:
string fileType=ConfigurationManager.AppSettings["FileType "];
2、<connectionStrings>節點
<connectionStrings>節點主要用於配置數據庫連接的,我們可以<connectionStrings>節點中增加任意個節點來保存數據庫連接字符串,將來在代碼中通過代碼的方式動態獲取節點的值來實例化數據庫連接對象,這樣一旦部署的時候數據庫連接信息發生變化我們僅需要更改此處的配置即可,而不必因為數據庫連接信息的變化而需要改動程序代碼和重新部署。數據庫鏈接示例如下:
<connectionStrings>
<add name="OraProfileConnString" connectionString="user id=admin;data source=CRMDB;password=123456;" providerName="System.Data.OracleClient"/>
</connectionStrings>
在代碼中我們可以這么實例化數據庫連接對象:
///1讀取web.config文件節點配置 string ConnectionStringProfile = ConfigurationManager.ConnectionStrings["OraProfileConnString"].ConnectionString; ///2實例化OracleConnection對象 OracleConnection conn = new OracleConnection(ConnectionStringProfile);
這樣做的好處是一旦開發時所用的數據庫和部署時的數據庫不一致,僅僅需要用記事本之類的文本編輯工具編輯connectionString屬性的值就行了。
自定義節點配置解析
經過查閱資料發現,有些人和我一樣,只用過我上面說的兩個節點,但是如果參數過多,這種做法的缺點也會明顯地暴露出來:appSetting中的配置參數項只能按key名來訪問,不能支持復雜的層次節點也不支持強類型, 而且由於全都只使用這一個集合,你會發現:完全不相干的參數也要放在一起!解決的方法便是使用自定義節點配置來解析。
我們來看一下如何在app.config或者web.config中增加一個自定義的配置節點。 在這篇博客中,我將介紹4種自定義配置節點的方式。
1、第一種情況——Property
配置文件如下,依照屬性的方式處理:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="Test1" type="Demo.Section1,Demo"/> </configSections> <Test1 UserName="aehyok" Path="www.cnblogs.com/aehyok"></Test1> </configuration>
自定義一個類,以ConfigurationSection為基類,各個屬性要加上[ConfigurationProperty] ,ConfigurationProperty的構造函數中傳入的name字符串將會用於config文件中,表示各參數的屬性名稱。
屬性的值的讀寫要調用this[],由基類去保存。
為了能使用配置節點能被解析,需要在<configSections>中注冊,代碼如上<section name="Test1" type="Demo.Section1,Demo"/>。

實現代碼如下:
namespace Demo { public class Section1 : ConfigurationSection { [ConfigurationProperty("UserName")] public string UserName { get { return this["UserName"].ToString(); } set { this["UserName"] = value; } } [ConfigurationProperty("Path")] public string Path { get { return this["Path"].ToString(); } set { this["Path"] = value; } } } }
下面將要介紹另三種配置節點雖然復雜一點,但是一些基礎的東西與這個節點是一樣的,所以后面我就不再重復說明了。
2、第二種情況——Element
配置文件如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="Test2" type="Demo.Section2,Demo"/> </configSections> <Test2> <Users UserName="aehyok" Password="123456"></Users> </Test2> </configuration>
實現代碼如下:
namespace Demo { public class Section2 : ConfigurationSection { [ConfigurationProperty("Users", IsRequired = true)] public SectionElement Users { get { return (SectionElement)this["Users"]; } } public class SectionElement : ConfigurationElement { [ConfigurationProperty("UserName", IsRequired = true)] public string UserName { get { return this["UserName"].ToString(); } set { this["UserName"] = value; } } [ConfigurationProperty("Password", IsRequired = true)] public string Password { get { return this["Password"].ToString(); } set { this["Password"] = value; } } } } }
第二種情況比第一種情況的區別就是,數據類型也是自己定義的,具體的配置屬性寫在ConfigurationElement的繼承類中。
3、第三種情況——CDATA
CDATA可以包含比較長的字符串,且可以包含HTML代碼段,這樣針對特殊字符的存放也比較方便。假如如下配置:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="Test3" type="Demo.Section3,Demo"/> </configSections> <Test3> <T1> <![CDATA[ Create proc _bank @param1 char(10),@param2 varchar(20),@param3 varchar(20),@param4 int,@param5 int output with encryption ---------加密 as bankMoney (id,userID,sex,Money) Values(@param1,@param2,@param3, @param4) @param5=sum(Money) bankMoney where userID=""Zhangsan"" go ]]> </T1> <T2> <![CDATA[ <html> <head> <title>Test</title> </head> <body> This is Test。 </body> </html> ]]> </T2> </Test3> </configuration>
代碼實現如下:
namespace Demo { public class Section3 : ConfigurationSection { [ConfigurationProperty("T1", IsRequired = true)] public MyTextElement Command1 { get { return (MyTextElement)this["T1"]; } } [ConfigurationProperty("T2", IsRequired = true)] public MyTextElement Command2 { get { return (MyTextElement)this["T2"]; } } } public class MyTextElement : ConfigurationElement { protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey) { CommandText = reader.ReadElementContentAs(typeof(string), null) as string; } protected override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey) { if (writer != null) writer.WriteCData(CommandText); return true; } [ConfigurationProperty("data", IsRequired = false)] public string CommandText { get { return this["data"].ToString(); } set { this["data"] = value; } } } }
這里由我們控制對數據的讀寫操作,也就是要重載方法SerializeElement,DeserializeElement。
4、第四種情況——Collection
配置信息如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="Test4" type="Demo.Section4,Demo"/> </configSections> <Test4> <add key="1" value="aehyok"></add> <add key="2" value="Leo"></add> <add key="3" value="Lynn"></add> </Test4> </configuration>
為每個集合中的參數項創建一個從ConfigurationElement繼承的派生類,可參考Section1。
為集合創建一個從ConfigurationElementCollection繼承的集合類,具體在實現時主要就是調用基類的方法。
在創建ConfigurationSection的繼承類時,創建一個表示集合的屬性就可以了,注意[ConfigurationProperty]的各參數。
實現代碼如下:
namespace Demo { public class Section4 : ConfigurationSection { private static readonly ConfigurationProperty s_property = new ConfigurationProperty(string.Empty, typeof(MyKeyValueCollection), null, ConfigurationPropertyOptions.IsDefaultCollection); [ConfigurationProperty("", Options = ConfigurationPropertyOptions.IsDefaultCollection)] public MyKeyValueCollection KeyValues { get { return (MyKeyValueCollection)base[s_property]; } } } [ConfigurationCollection(typeof(MyKeyValueSetting))] public class MyKeyValueCollection : ConfigurationElementCollection // 自定義一個集合 { // 基本上,所有的方法都只要簡單地調用基類的實現就可以了。 public MyKeyValueCollection() : base(StringComparer.OrdinalIgnoreCase) // 忽略大小寫 { } // 其實關鍵就是這個索引器。但它也是調用基類的實現,只是做下類型轉就行了。 new public MyKeyValueSetting this[string name] { get { return (MyKeyValueSetting)base.BaseGet(name); } } // 下面二個方法中抽象類中必須要實現的。 protected override ConfigurationElement CreateNewElement() { return new MyKeyValueSetting(); } protected override object GetElementKey(ConfigurationElement element) { return ((MyKeyValueSetting)element).Key; } // 說明:如果不需要在代碼中修改集合,可以不實現Add, Clear, Remove public void Add(MyKeyValueSetting setting) { this.BaseAdd(setting); } public void Clear() { base.BaseClear(); } public void Remove(string name) { base.BaseRemove(name); } } public class MyKeyValueSetting : ConfigurationElement // 集合中的每個元素 { [ConfigurationProperty("key", IsRequired = true)] public string Key { get { return this["key"].ToString(); } set { this["key"] = value; } } [ConfigurationProperty("value", IsRequired = true)] public string Value { get { return this["value"].ToString(); } set { this["value"] = value; } } }
讀取配置文件信息

四個Read進行讀取數據的代碼如下:
private void button1_Click(object sender, EventArgs e) { Section1 sectioin1 = (Section1)ConfigurationManager.GetSection("Test1"); txtUserName.Text = sectioin1.UserName; txtPath.Text = sectioin1.Path; } private void button4_Click(object sender, EventArgs e) { Section2 sectioin2 = (Section2)ConfigurationManager.GetSection("Test2"); txtUName.Text = sectioin2.Users.UserName; txtPassword.Text = sectioin2.Users.Password; } private void button6_Click(object sender, EventArgs e) { Section3 section3 = (Section3)ConfigurationManager.GetSection("Test3"); T1.Text = section3.T1.CommandText.Trim(); T2.Text = section3.T2.CommandText.Trim(); } private void button8_Click(object sender, EventArgs e) { Section4 section4 = (Section4)ConfigurationManager.GetSection("Test4"); txtKeyValues.Text = string.Join("", ( kv in section4.KeyValues.Cast<MyKeyValueSetting>() let s = string.Format("{0}={1}", kv.Key, kv.Value) s).ToArray()); }
執行點擊之后數據讀取如下圖所示,也就是數據讀取成功。

在讀取自定節點時,我們需要調用ConfigurationManager.GetSection()得到配置節點,並轉換成我們定義的配置節點類,然后就可以按照強類型的方式來訪問了。
寫入配置文件信息
首先定義一個全局變量,代碼如下
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
四個Write保存按鈕如下:
private void button2_Click(object sender, EventArgs e) { Section1 sectioin1 = config.GetSection("Test1") as Section1; sectioin1.UserName = txtUserName.Text; sectioin1.Path = txtPath.Text; config.Save(); } private void button3_Click(object sender, EventArgs e) { Section2 sectioin2 = config.GetSection("Test2") as Section2; sectioin2.Users.UserName = txtUName.Text; sectioin2.Users.Password = txtPassword.Text; config.Save(); } private void button5_Click(object sender, EventArgs e) { Section3 section3 = config.GetSection("Test3") as Section3; section3.T1.CommandText = T1.Text.Trim(); section3.T2.CommandText = T2.Text.Trim(); config.Save(); } private void button7_Click(object sender, EventArgs e) { Section4 section4 = config.GetSection("Test4") as Section4; section4.KeyValues.Clear(); ( s in txtKeyValues.Lines let p = s.IndexOf(""="") where p > 0 new MyKeyValueSetting { Key = s.Substring(0, p), Value = s.Substring(p + 1) } ).ToList() .ForEach(kv => section4.KeyValues.Add(kv)); config.Save(); }
在修改配置節點前,我們需要調用ConfigurationManager.OpenExeConfiguration(),然后調用config.GetSection()在得到節點后,轉成我們定義的節點類型, 然后就可以按照強類型的方式來修改我們定義的各參數項,最后調用config.Save();即可。 .net為了優化配置節點的讀取操作,會將數據緩存起來,如果希望使用修改后的結果生效,您還需要調用ConfigurationManager.RefreshSection方法。如果是修改web.config,則需要使用 WebConfigurationManager。
最終修改效果展示動態圖

總結
1、本文主要參考大神作品http://www.cnblogs.com/fish-li/archive/2011/12/18/2292037.html從中學習取經。
2、通過本文本人也學習到了很多關於配置文件的知識,之前都沒有接觸學習過。
3、打算再將其他的節點設置進行學習記錄下。
4、本文示例代碼鏈接http://pan.baidu.com/s/1o6JgUBg
5、ConfigSource的使用也比較有意思,自己之前整理的一篇文章,可惜在真實項目中沒用過。
例如:
<appSettings configSource="configMy.config" />
就是關於appSettings下的配置節點都配置到了configMy.config文件中。
有關博文鏈接http://www.cnblogs.com/aehyok/archive/2013/05/23/3095019.html
