【.net 深呼吸】自定義應用程序配置節
實際上,應用程序配置文件 App.config,是由各個節(Configuration Section)組成的,通常,配置節是按功能劃分的,比如我們很熟悉的 appSettings、connectionStrings、startup、system.ServiceModel……
在實際開發中,我們的應用程序也應該需要一個咱們程序專且功能的配置節,這樣也方便我們在代碼中通過相關的API來讀取,而不是用普通的XML文件讀取方法。
其實,實現自定義配置節並不難,只是,有幾個問題要註意。
老周一邊給大夥伴們演示一邊講解,這樣會比較有情調。
要自定義應用配置節,必須從 ConfigurationSection 類派生,該類是個抽象類,所以我們必須以其作為基類來定義自己的配置節。雖然它是一個抽象類,但是,功能很完善,多數情況下,我們是不需要重寫它的成員(特殊需求的話,可以按需重寫),給大家演示一下,看看我這個類。
public class MyDemoSection : ConfigurationSection { …… }
那麽,我們在這個類中要幹啥呢?我們要給它添加我們需要的屬性。比如,我需要兩個屬性——一個 User,字符串類型;另一個Age,類型為int。
public class MyDemoSection : ConfigurationSection { public string User {get {……} set { ……} } public int Age { get { ……} set { …… } } }
這時候,你一定會想到一個問題:那配置文件API在保存到配置文件時,是如何識別的呢。其實這個 ConfigurationSection 類的基類是 ConfigurationElement。ConfigurationElement 是所有配置文件元素的共同基類。配置節實則就是一種特殊的配置元素,它與普通的配置元素的區別在於,配置節通常作為一類功能的主節點。而配置節的子元素就是普通的配置元素;配置節在使用前必須在 configSections 節點下進行聲明,聲明後才能在配置文件中使用。這個咱們後面再談。
ConfigurationElement 類公開了以下兩個版本的索引器:
protected internal object this[string propertyName] { get; set; } protected internal object this[ConfigurationProperty prop] { get; set; }
我們一般用的是帶string參數的版本,這個索引器只能在派生類中訪問,不對外部公開。而 ConfigurationSection 類是從 ConfigurationElement 類派生的,自然會繼承這個索引器。
所以,我們在自定義配置節的屬性包裝中,可以通過這個索引器來存取內容,其操作方法類似於字典。
public class MyDemoSection : ConfigurationSection { public string User { get { return (string)this["user"]; } set { this["user"] = value; } } public int Age { get { return (int)this["age"]; } set { this["age"] = value; } } }
代碼寫到這裏,貌似是完成了,其實未然。這時候,如果你想用代碼把這個自定義節點寫入配置文件,就會收到以下異常。
因為我們的自定義配置節類還沒有完工,還差一個 Attribute 沒有應用。應該這樣寫。
[ConfigurationProperty("user")] public string User { get { return (string)this["user"]; } set { this["user"] = value; } } [ConfigurationProperty("age")] public int Age { get { return (int)this["age"]; } set { this["age"] = value; } }
註意,嚴重要註意!!應用的ConfigurationPropertyAttribute 中指定的name一定要和索引器(this["....."])中使用的名字相同,比如上面的代碼,age要一致,如果改為這樣,就會報錯。
[ConfigurationProperty("number")] public int Age { get { return (int)this["age"]; } set { this["age"] = value; } }
因為 number 和 age 不匹配,不過,類型的屬性名不要求,比如,Age屬性可以改為 Num。
[ConfigurationProperty("age")] public int Num { get { return (int)this["age"]; } set { this["age"] = value; } }
把屬性名改為 Num 是不會報錯的。只要 ConfigurationPropertyAttribute 中的 name 參數與索引器中的名字一致即可。
下面,我們試試這個自定義配置節,我們通過代碼來配置,然後保存到 App.Config 中。
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); MyDemoSection sect = new MyDemoSection(); config.Sections.Add("myInfo", sect); //sect.SectionInformation.ForceSave = false; sect.User = "Ben Dan"; sect.Age = 25; // 保存 config.Save(ConfigurationSaveMode.Modified);
通過....Sections.Add 方法就可以把自定義的配置節添加配置文件中,其中,有一個name 參數,它用來指定在使用該配置時的XML元素名稱。這個你如果不理解,不急,你先記住我們代碼中寫的名字叫 myInfo。
然後執行一下上面的代碼,再打開與應用程序相關的App.config文件,註意不是項目中的config文件,是bin\\ 目錄下的。打開後你會看到這樣的內容。
<configuration> <configSections> <section name="myInfo" type="CustConfigs.MyDemoSection, CaiDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </configSections>
<myInfo user="Ben Dan" age="25" />
…… </configuration>
怎麽樣,你現在看明白了沒,知道那個 myInfo 怎麽用了吧。
首先,要在 configSections 節中用 section 元素來配置一下自定義配置節的類型,type指定的就是我們剛剛定義的那個 MyDemoSection 類,其實只需要寫上類型名和程序集名即可。name 指定一個名稱,就是你隨後在配置文件使用時的XML元素名,如本例中的 myInfo。
在上面的代碼中,可能你註意到有一行代碼被註釋了,
sect.SectionInformation.ForceSave = false;
MSDN 上說,這個屬性指示是否要強制保存配置節的內容,哪怕它沒有被修改過,這裏我用不上,就註釋掉了,因為MSDN上的示例有這一行,我故意加上來裝逼一下的。
剛才,我們通過代碼應用我自定義配置節,內容已寫入 app.config 文件,此時我們如果再次運行上面的代碼,你會發現報錯了。
原來配置節是不能重復 Add 的,所以我們改改代碼,先進行判斷。
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); MyDemoSection sect = config.Sections["myInfo"] as MyDemoSection; if(sect == null) { sect = new MyDemoSection(); config.Sections.Add("myInfo", sect); } sect.User = "Ben Dan"; sect.Age = 25; // 保存 config.Save(ConfigurationSaveMode.Modified);
好了,這樣可避免報錯了。
接下來,老周再給各位舉一例,這一次我們不用代碼來寫配置文件,而是直接編輯配置文件,然後在代碼中讀出配置信息。這種做法應該最常用。
還是那樣,我們自定義一個配置節類型。
public class DBSection : ConfigurationSection { [ConfigurationProperty("dbName", DefaultValue = "db.mdf")] public string DBName { get { return (string)this["dbName"]; } set { this["dbName"] = value; } } [ConfigurationProperty("loginName", DefaultValue = "sa")] public string LoginName { get { return (string)this["loginName"]; } set { this["loginName"] = value; } } }
假設,DBName 表示數據庫文件的名字,LoginName表示登入 MSSQL 的名稱(默認是 sa)。
然後,我們打開\bin\debug 下面的app.config,註意,不是項目中的配置文件。先為自定義配置節進行聲明。
<configSections> <section name="db" type="CustConfigs.DBSection, CaiDemo"/> </configSections>
這時候,要配置的話,XML元素名為db。
<db dbName ="app.mdf" loginName="admin" />
註意這裏設置的屬性名不是類上的屬性名,而是 ConfigurationPropertyAttribute 所指定的名稱。
現在,我們在代碼中讀出這些配置參數。
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); DBSection sect = config.Sections["db"] as DBSection; if (sect != null) { string s = $"數據庫:{sect.DBName},登錄名:{sect.LoginName}。"; Console.WriteLine(s); }
註意剛剛我們在配置文件中聲明時,給自定義節的元素命名為 db 。
<section name="db" type="CustConfigs.DBSection, CaiDemo"/>
所以,我們讀的時候,也要用 db 作為名字取出配置節實例,然後再讀屬性的值。
最後,輸出結果如下:
好了,今天的內容就扯到這裏吧,這個玩意兒,大家學會之後,還是很有實戰價值的,而且也不難。
【.net 深呼吸】自定義應用程序配置節