做項目時,經常需要在自己設計的類庫中使用很多用戶配置。雖然在應用程序的App.config和Web應用程序web.config這樣的文件里配置也能滿足需求,但這樣做不僅會讓主配置文件的內容變得多、雜,還會讓模塊依賴主程序的配置文件。
我們知道在VS中,可以在類庫項目里添加一種叫做“應用程序配置文件”的文件,這是標准的.NET配置文件,模板自帶“configuration”元素,編輯時還會有智能提示。但是怎么在程序代碼中使用寫在App.config里的配置呢?近日在網上搜了一通,卻一無所獲。於是只好自已動手!
我以前做的一個項目里,用到過類型的實現方式。可以獲取在類庫App.config文件中“appSettings”和“conectionStrings”節添加的自定義配置,但是不能自定義配置節。從MSDN上了解到,要想在配置文件中自定義配置節,需要實現一個自定義的ConfigurationSection。兩下結合起來,想在類庫中用App.config徹底自定義配置的需求就可以實現了。
現在分享出來,希望對看到這篇文章的朋友有所幫助。
第一步:創建項目和類庫:
新建一個Windows控制台應用程序“MyDemo”,然后再新建一個C#類庫“MyDemo.Config”,並在MyDemo中添加對MyDemo.Config的引用。
第二步:添加引用,新建配置文件:
在MyDemo.Config中先刪除除System之外的所有引用,然后添加對System.Configuration庫的引用,並新建一個配置文件App.config。
第三步:在MyDemo.Config里面添加一個靜態類“ConfigManager”,代碼里這樣寫:

using System; using System.Configuration; namespace MyDemo.Config { public static class ConfigManager { readonly static bool _Error; static Configuration _AppConfig; static ConfigManager() { string dllPath = string.Format( "{0}\\{1}.dll", AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory, "MyDemo.Config"); try { _AppConfig = ConfigurationManager.OpenExeConfiguration(dllPath); } catch(ConfigurationErrorsException) { _Error = true; } } public static KeyValueConfigurationCollection AppSettings { get { if (_Error) return null; return _AppConfig.AppSettings.Settings; } } public static ConnectionStringSettingsCollection ConnectionStrings { get { if (_Error) return null; return _AppConfig.ConnectionStrings.ConnectionStrings; } } } }
通過AppDomain.CurrentDomain.BaseDirectory和穩定的類庫名稱,來獲取實際運行中該dll文件的具體物理路徑,然后通過ConfigurationManager的OpenExeConfiguration方法就能獲取到相應的dll.config文件中的配置。
為了保險起見,我增加了一個_Error字段,在靜態構造器中使用了try語句,這樣能保證程序的順序運行!
通過這個ConfigManager,可以直接獲取在App.config中添加的appSeetings和connectionStrings的配置。
第四步:添加對自定義的配置節的支持
要自定義配置節,需要實現ConfigurationSection。這個其實MSDN上就有很好的例子,我這里做一個簡單的實現,比如我要增加這樣的配置:
<smtp host="smtp.163.com" mail="abc@163.com" pass="123456"></smtp>
我們來新建一個SmtpSection,繼承自ConfigurationSection。重寫基類的IsReadOnly()方法,表示這些配置是只讀的。
然后我們添加幾個只讀的屬性:Host(string)、Mail(string)、Password(string)、Port(int);get訪問器的實現很特殊,要用this["自定義配置節的attribute名稱"]這種方法,還要轉換成相應屬性的具體類型。所有將在配置中出現的屬性,都要加上ConfigurationProperty標記。
來看具體的實現:

using System; using System.Configuration; namespace MyDemo.Config { public class SmtpSection : ConfigurationSection { public override bool IsReadOnly() { return true; } [ConfigurationProperty("host", IsRequired = true)] public string Host { get { return this["host"] as string; } } [ConfigurationProperty("mail", IsRequired = true)] //[RegexStringValidator(可以在這里用正則驗證配置文件中的值是否正確)] public string Mail { get { return this["mail"] as string; } } [ConfigurationProperty("pass", IsRequired = true)] public string Password { get { return this["pass"] as string; } } [ConfigurationProperty("port", IsRequired = false, DefaultValue = 25)] [IntegerValidator(MaxValue = 65535, MinValue = 1)] public int Port { get { return (int)this["port"]; } } } }
可以看到,在聲明屬性的ConfigurationProperty標記時,不但可以指定此屬性將在配置文件中出現的的名稱,還可以指定默認值、是否必須屬性等。字符串還可以使用RegexStringValidator標記來驗證用戶輸入的配置是否合法;數字則可以用IntegerValidator來規定用戶輸入的數字范圍。最貼心的是這些合法性的驗證,都是在編譯時進行的,而不是在程序運行時。
第五步:在App.Config中添加自定義配置節
新增一個configSections節,在下面添加自定義的section,nam的值是下面的xml元素的開始和結束標記,type的值的格式是這樣的“自定義配置節類型的完全名稱(包括命名空間), 命名空間”。這里增加一個appSetting和connectionString以便稍后測試。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="smtp" type="MyDemo.Config.SmtpSection, MyDemo.Config" /> </configSections> <smtp host="smtp.163.com" mail="abc@163.com" pass="123456"></smtp> <appSettings> <add key="domain" value="dream.net" /> </appSettings> <connectionStrings> <add name="default" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|NORTHWND.MDF;Integrated Security=True;Connect Timeout=30" /> </connectionStrings> </configuration>
第六步:修改ConfigManager,添加獲取自定義配置節smtp的方法
給ConfigManager類增加一個方法,為了能最大限度的重用這個方法,我把方法寫成了泛型方法! GetSection<T>(string name);
public static T GetSection<T>(string name) where T : ConfigurationSection { if (_Error) return null; return _AppConfig.GetSection(name) as T; }
第七步:測試
要想使這個配置起使用,前提是App.config文件必須以“dll名稱.dll.config”的文件名存在應用程序的執行目錄中。生成類庫的時候,在類庫的輸出目錄會自動生成這個配置文件,但是每次更新配置文件都要把配置文件復制一遍很麻煩。解決辦法就是,把App.config改名。比如上面提到的MyDemo.Config中的App.config就可以改成“MyDemo.Config.dll.config”,然后設置屬性“復制到輸出目錄”為“較新則復制”。這樣,每次修改配置文件后,只要重新生成,配置文件就會被自動復制到應用程序的執行目錄中。
解決了這個問題,我們在MyDemo中寫一段代碼測試一下,因為ConfigManager的一些屬性和方法的返回值的類型位於System.Configuration命名空間,所以,要使用它必須在程序中也添加對System.Configuration的引用。當然,你也可以改變一下獲取appSettings和connectionStrings的方式,直接返回配置的值,而不是配置集合!
using MyDemo.Config;
class Program { static void Main(string[] args) { Console.WriteLine(ConfigManager.AppSettings["domain"].Value); Console.WriteLine(ConfigManager.ConnectionStrings["default"].ConnectionString); var smtp = ConfigManager.GetSection<MyDemo.Config.SmtpSection>("smtp"); Console.WriteLine(smtp.Host); Console.WriteLine(smtp.Mail); Console.WriteLine(smtp.Password); Console.WriteLine(smtp.Port); Console.Read(); } }
運行結果:
通過結果,可以確定配置在config里面的設置都已經被順利的讀出來了;這是ConfigurationSection的實現,另外,如果要實現的配置具有復雜數據結構,還要根據需要實現ConfigurationElement,具體實現請參考MSDN的例子 http://msdn.microsoft.com/zh-cn/library/2tw134k3(v=vs.100).aspx
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
PS:可以對例子的ConfigManager做一些改造,使之成為項目配置存取模塊(Configuration是支持讀寫的)。另外,對於一些不想這么麻煩的懶人,我推薦一款免費的VS插件,叫“Configuration Section Designer”,安裝插件后會安裝一個配置節的設計模板,允許你像設計UML圖一樣設計你的配置,插件會幫你自動生成具體的實現,非常的實用。