有經驗的開發人員都知道在開發.NET應用時可以利用配置文件保存一些常用並且有可能變化的信息,例如日誌文件的保存路徑、數據庫連接信息等等,這樣即使生產環境中的參數信息與開發環境不一致也只需要更改配置文件而不用改動源代碼再重新編譯,極其方便。並且我們一般還約定,在<appSettings>節點保存應用程序的配置信息,在<connectionStrings>中保存數據庫連接字符串信息(詳見本博客《asp.net夜話之十一:web.config詳解》)。
上面的這些方法和約定足以讓我們在大部分開發中獲得方便,但是在有些情況下有些配置信息可以按組分類存放,如果採用上面的方法不僅不直觀,而且讀取起來也不是太方便,幸好在.NET裏就提供了這樣的方法。如果有使用過Log4Net或者Enyim.Caching的朋友,肯定對下面的配置不會陌生:
- <sectionGroup name="enyim.com"><section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching" /></sectionGroup>
或:
- <configSections><section name="log4net" type="System.Configuration.IgnoreSectionHandler"/></configSections>
在出現上面配置的配置文件中,我們就會找到名稱爲"enyim.com"或者"log4net"的節點,儘管它們本不屬於config文件的默認節點,但是通過上面的配置之後程序運行並不會報錯。這樣一來,相關配置信息也可以很好分類保存起來。
在這裏周公演示一個簡單的例子,這個例子來源於周公的一個從2006年起就開始開發的自用軟件(因爲沒有美化所以沒有免費發佈),在這個應用程序的connfig文件中我增加了一些特有的配置,所以新增了一個自己的節點,app.config文件內容如下:
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <configSections>
- <section name="SoftwareSettings" type="ImageAssistant.Configuration.SoftwareSettings, ImageAssistant" />
- </configSections>
- <SoftwareSettings>
- <LoadSettings>
- <add key="LoadBmp" value="true"/>
- <add key="LoadJpg" value="true"/>
- <add key="LoadGif" value="true"/>
- <add key="LoadPng" value="false"/>
- </LoadSettings>
- <PathSettings SavePath="C:/ResizeImages/" SearchSubPath="true"/>
- </SoftwareSettings>
- <appSettings>
- <add key="LoadBmp" value="true"/>
- <add key="LoadJpg" value="true"/>
- <add key="LoadGif" value="true"/>
- <add key="LoadPng" value="false"/>
- <add key="IncludeSubPath" value="true"/>
- </appSettings>
- </configuration>
在config文件中我們使用<section name="SoftwareSettings" type="ImageAssistant.Configuration.SoftwareSettings, ImageAssistant" />告訴應用程序對於配置文件中的SoftwareSettings節點,其對應的類是ImageAssistant程序集中ImageAssistant.Configuration.SoftwareSettings類,並且在<SoftwareSettings>節點中我們還看到有<LoadSettings>節點和<PathSettings>節點,其中<LoadSettings>是一個節點集合,還包含有多個子節點,爲了表示清楚這些關係我們需要添加四個類:SoftwareSettings、LoadSettingsCollection、LoadSettingsElement及PathSettingElement。爲了發佈方便,周公將這四個類的代碼放在一個物理文件中,代碼如下(注意添加對System.Configuration.dll的引用):
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Configuration;
- //作者:zhoufoxcn(周公)
- //日期:2011-03-08
- //blog:http://blog.csdn.net/zhoufoxcn 或http://zhoufoxcn.blog.51cto.com
- //版權聲明:本文允許非商業用途,但必須保證不得去掉本文中的任何鏈接,違者必究。
- namespace ImageAssistant.Configuration
- {
- public sealed class LoadSettingsCollection : ConfigurationElementCollection
- {
- private IDictionary<string, bool> settings;
- protected override ConfigurationElement CreateNewElement()
- {
- return new LoadSettingsElement();
- }
- protected override object GetElementKey(ConfigurationElement element)
- {
- LoadSettingsElement ep = (LoadSettingsElement)element;
- return ep.Key;
- }
- protected override string ElementName
- {
- get
- {
- return base.ElementName;
- }
- }
- public IDictionary<string, bool> Settings
- {
- get
- {
- if (settings == null)
- {
- settings = new Dictionary<string, bool>();
- foreach (LoadSettingsElement e in this)
- {
- settings.Add(e.Key, e.Value);
- }
- }
- return settings;
- }
- }
- public bool this[string key]
- {
- get
- {
- bool isLoad = true;
- if (settings.TryGetValue(key, out isLoad))
- {
- return isLoad;
- }
- else
- {
- throw new ArgumentException("沒有對'" + key + "'節點進行配置。");
- }
- }
- }
- }
- public class LoadSettingsElement : ConfigurationElement
- {
- [ConfigurationProperty("key", IsRequired = true)]
- public string Key
- {
- get { return (string)base["key"]; }
- set { base["key"] = value; }
- }
- [ConfigurationProperty("value", IsRequired = true)]
- public bool Value
- {
- get { return (bool)base["value"]; }
- set { base["value"] = value; }
- }
- }
- public class PathSettingElement : ConfigurationElement
- {
- /// <summary>
- ///
- /// </summary>
- [ConfigurationProperty("SavePath", IsRequired = true)]
- public string SavePath
- {
- get { return (string)base["SavePath"]; }
- set { base["SavePath"] = value; }
- }
- /// <summary>
- ///
- /// </summary>
- [ConfigurationProperty("SearchSubPath", IsRequired = false, DefaultValue = true)]
- public bool SearchSubPath
- {
- get { return (bool)base["SearchSubPath"]; }
- set { base["SearchSubPath"] = value; }
- }
- }
- /// <summary>
- /// 對應config文件中的
- /// </summary>
- public sealed class SoftwareSettings : ConfigurationSection
- {
- /// <summary>
- /// 對應SoftwareSettings節點下的LoadSettings子節點
- /// </summary>
- [ConfigurationProperty("LoadSettings", IsRequired = true)]
- public LoadSettingsCollection LoadSettings
- {
- get { return (LoadSettingsCollection)base["LoadSettings"]; }
- }
- /// <summary>
- /// 對應SoftwareSettings節點下的PathSettings子節點,非必須
- /// </summary>
- [ConfigurationProperty("PathSettings", IsRequired = false)]
- public PathSettingElement PathSetting
- {
- get { return (PathSettingElement)base["PathSettings"]; }
- set { base["PathSettings"] = value; }
- }
- }
- }
在上面的代碼中可以看到ConfigurationProperty這個屬性,這是表示對應的屬性在config文件中的屬性名,IsRequired表示是否是必須的屬性,還有DefaultValue表示屬性的默認值。初次之外,我們還要注意以下關係:
SoftwareSettings:根節點,繼承自ConfigurationSection。
LoadSettingsCollection:子節點集合,繼承自ConfigurationElementCollection。
LoadSettingsElement:子節點,繼承自ConfigurationElement。
PathSettingElement:子節點,繼承自ConfigurationElement。
編寫了如下代碼之後,我們又該如何使用上面的類呢?其實很簡單,如下:
- class Program
- {
- static void Main(string[] args)
- {
- SoftwareSettings softSettings = ConfigurationManager.GetSection("SoftwareSettings") as SoftwareSettings;
- foreach (string key in softSettings.LoadSettings.Settings.Keys)
- {
- Console.WriteLine("{0}={1}", key, softSettings.LoadSettings[key]);
- }
- Console.WriteLine("SavePath={0},SearchSubPath={1}", softSettings.PathSetting.SavePath, softSettings.PathSetting.SearchSubPath);
- Console.ReadLine();
- }
- }
這個程序的運行結果如下:
LoadBmp=True
LoadJpg=True
LoadGif=True
LoadPng=False
SavePath=C:/ResizeImages/,SearchSubPath=True
總結:在上面的config文件中通過<appSettings>也達到了類似的效果,但是通過自定義節點我們可以方便地讀取相關的應用程序配置,同時也便於維護。如果在開發過程中遇到本文中類似的情況,不妨採取本文所述的方式。本文示例源代碼可以到這裏下載。
周公
2011-03-08