let's go on!
在我研究和實驗配置節的時候,我學到一些技巧,可是使他們更容易使用。自定義配置節的某些方面非常乏味,如所有時間一直通過調用<code>ConfigurationManager.GetSection("someSectionName")擷取SomeSectionClass。為了減輕繁瑣乏味,我嘗試在我的<code>ConfigurationSection</code>類中使用下列方式:</code>
public class SomeConfigurationSection
{
static SomeConfigurationSection()
{
// Preparation...
}
// Properties...
#region GetSection Pattern
private static SomeConfigurationSection m_section;
/// <summary>
/// Gets the configuration section using the default element name.
/// </summary>
public static SomeConfigurationSection GetSection()
return GetSection("someConfiguration");
/// Gets the configuration section using the specified element name.
public static SomeConfigurationSection GetSection(string definedName)
if (m_section == null)
{
m_section = ConfigurationManager.GetSection(definedName) as
SomeConfigurationSection;
if (m_section == null)
throw new ConfigurationException("The <" + definedName +
"> section is not defined in your .config file!");
}
return m_section;
#endregion
}
上述的模式增加了一個靜态<code>GetSection()</code>方法給每個自定義<code>ConfigurationSection</code>類。它的一個重載方法,以一個字元串作為參數,允許你為.config中元素定義一個不同的名字,如果你喜歡的話。另外,預設的重載是可以使用的。這種模式使用在标準的應用程式(.exe)的配置節下工作的非常好。然而,如果配置節使用在一個web.config檔案中,你将需要使用下面的代碼:
using System.Web;
using System.Web.Configuration;
/// <remarks>
/// If an HttpContext exists, uses the WebConfigurationManager
/// to get the configuration section from web.config.
/// </remarks>
{
string cfgFileName = ".config";
if (HttpContext.Current == null)
{
m_section = ConfigurationManager.GetSection(definedName)
as SomeConfigurationSection;
}
else
m_section = WebConfigurationManager.GetSection(definedName)
cfgFileName = "web.config";
"> section is not defined in your " +
cfgFileName + " file!");
正如你看到的,在ASP.NET下通路配置節需要使用<code>System.Web.Configuration.WebConfigurationManager</code>,而不是<code>System.Configuration.ConfigurationManager</code>。檢查目前的<code>HttpContext</code>足以确定使用哪個管理器(manager)去擷取配置節。還有一個增強,我們對這種模式,使得允許我們可以儲存修改。我們知道,必須使用<code>Configuration</code>對象來儲存修改,因為管理器(manager)類不提供<code>Save()</code>方法。我們可以在我們的GetSection方法中建立一個<code>Configuration</code>對象,但是最終将缺乏靈活性、效率不高。在最終完全的模式中,我這樣做,把<code>Configuration</code>對象作為一個參數:
// Dictionary to store cached instances of the configuration object
private static Dictionary<string,
SomeConfigurationSection> m_sections;
/// Finds a cached section with the specified defined name.
private static SomeConfigurationSection
FindCachedSection(string definedName)
if (m_sections == null)
m_sections = new Dictionary<string,
SomeConfigurationSection>();
return null;
}
SomeConfigurationSection section;
if (m_sections.TryGetValue(definedName, out section))
return section;
return null;
/// Adds the specified section to the cache under the defined name.
private static void AddCachedSection(string definedName,
SomeConfigurationSection section)
if (m_sections != null)
m_sections.Add(definedName, section);
/// Removes a cached section with the specified defined name.
public static void RemoveCachedSection(string definedName)
m_sections.Remove(definedName);
/// to get the configuration section from web.config. This method
/// will cache the instance of this configuration section under the
/// specified defined name.
if (String.IsNullOrEmpty(definedName))
definedName = "someConfiguration";
SomeConfigurationSection section = FindCachedSection(definedName);
if (section == null)
section = ConfigurationManager.GetSection(definedName)
as SomeConfigurationSection;
section = WebConfigurationManager.GetSection(definedName)
if (section == null)
"> section is not defined in your " + cfgFileName +
" file!");
AddCachedSection(definedName, section);
return section;
/// Gets the configuration section using the default element name
/// from the specified Configuration object.
public static SomeConfigurationSection GetSection(Configuration config)
return GetSection(config, "someConfiguration");
/// Gets the configuration section using the specified element name
public static SomeConfigurationSection GetSection(Configuration config,
string definedName)
if (config == null)
throw new ArgumentNullException("config",
"The Configuration object can not be null.");
SomeConfigurationSection section = config.GetSection(definedName)
as SomeConfigurationSection;
throw new ConfigurationException("The <" + definedName +
"> section is not defined in your .config file!");
通過傳遞<code>Configuration</code>對象,一個可儲存的配置節執行個體能在XML檔案中檢索一個指定名字的配置節。這把我帶到另外一個重要的配置節秘訣。配置節元素的名字不一定必須的固定不變,也不一定隻有一個配置節的執行個體。在一個.config檔案按中每個配置節可以定義和設定多次,隻要給每個執行個體不同的名字:
<configuration>
<configSections>
<section name="example1" type="Examples.Configuration.ExampleSection,
Examples.Configuration" />
<section name="example2" type="Examples.Configuration.ExampleSection,
<section name="example3" type="Examples.Configuration.ExampleSection,
</configSections>
<example1 />
<example2 />
<example3 />
</configuration>
以同樣的方式配置節組也可以定義多次。這使得一個通常的自定義配置結構在同一個應用程式中,同時以多種方式使用,而且使得自定義配置可以重用。因為可能在一個.config檔案中一個配置節定義多次,最終實作上述的模式包括一個簡單的執行個體緩存。每次用不同的<code>definedName</code>調用<code>GetSection(string)</code>,将傳回不同的配置節對象且存儲在緩存中。連續以相同的名字調用将傳回相同的緩存執行個體。這種模式的另一個重要方面是缺少為兩個新版本的<code>GetSection</code>(以一個<code>Configuration</code>對象作為參數的<code>GetSection</code>方法)的緩存。用<code>Configuration</code>對象比用<code>ConfigurationManager</code>或WebConfigurationManager花費更大的開銷。通過配置管理器(manager)調用<code>GetSection()</code>方法将完全緩存節,而通過<code>Configuration</code>對象調用将導緻節每次都要被解析。一般來說,<code>Configuration</code>對象隻有當需要儲存配置更改是才使用。配置管理器類應該被用來通路讀取配置。這将保證使用配置設定時性能最佳。
最後一個秘訣是關于性能的主題。除非你實作進階的配置節或元素,包括事件通知,緩存配置設定在變量中通常沮喪的(行不通的)。更多關于這個的讨論将在下面的進階部分,首先考慮以下非常簡單的情景:
public class SomeProgram
static Main()
s_config = MyConfig.GetSection();
s_duration = s_config.Duration;
s_timer = new Timer(
new TimerCallback(),
null,
TimeSpan.Zero
s_duration
);
Console.ReadKey();
s_timer.Dispose();
private static MyConfig s_config;
private static Timer s_timer;
private static TimeSpan s_duration;
private static void WriteCurrentTime(object data)
Console.WriteLine("The current time is " + DateTime.Now.ToString());
if (s_duration != s_config.Duration)
s_duration = s_config.Duration;
s_timer.Change(TimeSpan.Zero, s_duration);
在上面的應用程式中,我們希望如果配置檔案更新的話,改變定時器間隔。我們配置節的執行個體,s_config,将一直保持更新。是以如果在應用程式運作時,.config檔案改變,任何改變将被發現并載入到記憶體中。如果你跟我在文章中一樣實作你的配置節,覆寫靜态構造器和替換屬性(properties)集合,這樣你的集合将有有高的性能。這使得通路一個配置屬性(property)相對廉價的操作,是以上述代碼可以重寫成如下:
s_config.Duration
Console.WriteLine("The current time is " +
DateTime.Now.ToString());
s_timer.Change(TimeSpan.Zero, s_config.Duration);
如果這個例子過于簡單揭示直接使用配置設定的意義,那麼想象一個更複雜的場景,一個配置值在一個緩存變量。變量是順序通過一個鍊來調用,然後循環使用。如果想要的結果對任何配置的過期及時發現并回答,那麼緩存将不起作用。你必須直接通路配置屬性(property),不用把它緩存到變量中。配置設定是全局通路的,可以在應用程式的任何地方。這意味着在你的代碼中的任何地方都可以通路配置屬性(property),而不用緩存變量的值和傳遞一個變量參數。将使得代碼更幹淨,因為你要求更少的的參數。如果高性能是絕對必要的,是有可能寫一個有事件的配置節,當配置資料在磁盤上已經改變能通知使用者。然而,這是一個更進階的主題,将在以後讨論。
本文中概述的資訊提供了一個全面地介紹.NET 2.0架構的配置功能特性。然而,這決不是一個全面的檔案,并且還有一些更複雜的使用配置節。其他資訊将在後面的文章:
解碼.NET 2.0配置之謎
破解.NET 2.0配置之謎
12.1、附錄A: 配置結構的級聯
在ASP.NET應用程式中,web.config檔案可能針對任何IIS“應用程式”。倘若應用程式的虛拟檔案夾是另一個應用程式的孩子,來自父應用程式的web.config檔案将和子應用程式的web.config合并。因為IIS中的應用程式可以嵌套任何級别的深度,當子應用應程式的web.config加載時,配置級聯将産生。
假設我們有一個站點安裝在IIS裡,以下面的層次結構且每個web.config檔案包含一個共同的集合:
\wwwroot
web.config
\firstapp
web.config
\anotherapp
\childapp
web.config
\finalapp
<!-- \wwwroot\web.config -->
<commonCollection>
<add key="first" value="C98E4F32123A" />
<add key="second" value="DD0275C8EA1B" />
<add key="third" value="629B59A001FC" />
</commonCollection>
<!-- \wwroot\firstapp\web.config -->
<remove key="first" />
<add key="first" value="FB54CD34AA92" />
<add key="fourth" value="DE67F90ACC3C" />
<!-- \wwroot\anotherapp\web.config -->
<add key="fourth" value="123ABC456DEF" />
<add key="fifth" value="ABC123DEF456" />
<add key="sixth" value="0F9E8D7C6B5A" />
<!-- \wwroot\anotherapp\childapp\web.config -->
<remove key="second" />
<remove key="fourth" />
<remove key="sixth" />
<add key="seventh" value="ABC123DEF456" />
<add key="ninth" value="0F9E8D7C6B5A" />
<!-- \wwroot\lastapp\web.config -->
<clear />
<add key="first" value="AABBCCDDEEFF" />
<add key="second" value="112233445566" />
<add key="third" value="778899000000" />
<add key="fourth" value="0A0B0C0D0E0F" />
如果我們研究了每個應用程式的集合,結果将如下:
\wwwroot\web.config
first = C98E4F32123A
second = DD0275C8EA1B
third = 629B59A001FC
\wwwroot\firstapp\web.config
first = FB54CD34AA92
fourth = DE67F90ACC3C
\wwwroot\anotherapp\web.config
fourth = 123ABC456DEF
fifth = ABC123DEF456
sixth = 0F9E8D7C6B5A
\wwwroot\anotherapp\childapp\web.config
seventh = ABC123DEF456
ninth = 0F9E8D7C6B5A
\wwwroot\lastapp\web.config
first = AABBCCDDEEFF
second = 112233445566
third = 778899000000
fourth = 0A0B0C0D0E0F
我希望這個簡單的示例,子應用程式的web.config是如何繼承設定的,這些設定是如何被覆寫,足夠了。你可能不是經常遇到這種情況,但是了解發生了什麼,以及如何覆寫父web.config的配置設定,應該有助于減輕ASP.NET開發者的配置檔案問題。
12.2、附錄B: 包含外部配置檔案
盡管在.NET 2.0的配置功能中都很偉大,但是仍有一個缺點。當工作在一個多環境的單一項目中,管理配置檔案是一個噩夢。管理多環境下的多版本的配置檔案(如開發、測試、階段、産品)的過程,我目前的工作包括手工比較.config檔案,将更改部署到一個環境或另外一個,通過手工合并。我花了幾個月試圖找到一種更好的方法,最終找到了。進入這樣那樣一些沒有“沒有文檔的”或很少文檔的——微軟著名的特點,的其中的一個:configSource。當我用Reflector深入挖掘.NET 2.0配置源碼的時候,碰到這個珍品,美妙的小工具。
每個配置節在被.NET配置類解析和加載時,都配置設定了一個<code>SectionInformation</code>對象。<code>SectionInformation</code>對象包含關于配置節的元資訊,并允許管理節如何互相覆寫,當定義在一個子web.config中時(ASP.NET)。現在,我們将忽略大部分<code>SectionInformation</code>對象提供的,考慮configSource屬性(property)。通過添加configSource屬性(attribute)到任何<code>ConfigurationSection的</code>根元素,你可以指定一個備用,外部的配置設定将被加載。
<configuration>
<connectionStrings configSource="externalConfig/connectionStrings.config"/>
</configuration>
<!-- externalConfig/connectionStrings.config -->
<connectionStrings>
<add name="conn" connectionString="blahblah" />
</connectionStrings>
在上面的配置檔案中,<code><connectionStrings></code>節源于名為externalConfig/connectionStrings.config的檔案。所有應用程式的連接配接字元串将加載自這個特定檔案。現在,連接配接字元串是從外部資源加載的,在相對相同位置的每個環境,他相對簡單地建立一個connectionStrings.config檔案。是以externalConfig/ connectionStrings.config 檔案的路徑。這裡漂亮的地方是,我們可以正确地為每個環境定義連接配接字元串定義一次。我們不用擔心意外覆寫那些設定,在部署一個config檔案時,無論是否合并得當或根本不合并。這是一個巨大的福音,當更改一個應用程式到産品環境是,他的關鍵正确的資料庫連接配接字元串存在。使用configSource屬性(attribute)失效,就是它要求所有的配置節将放置在外部檔案中。沒有繼承或覆寫是可能的,在某些情況下使它沒用。所有的外部配置檔案用configSource屬性(attribute)引用,也必須放在相對子到主的.config檔案路徑上。我相信這是考慮web環境中的安全性,存儲檔案在相對父路徑上。
别的需要注意的是<code><appSettings></code>節有一個更好的選擇使用configSource,稱為<code>file</code>。如果你使用<code>file</code>屬性(attribute)而不是configSource在<code><appSettings></code>節裡,你可以定義設定在根.config檔案或引用檔案都可以。根.config檔案的設定也能被引用檔案覆寫,簡單地用相同的鍵添加東西。可悲的是,<code>file</code>屬性(attribute)隻适用在<code><appSettings></code>節,而不是建立在配置架構下。在我們自己的配置節中也可能實作類似的屬性(attribute)。這将在将來的進階配置主題部分讨論,幾個先決部分之後。
請繼續關注!
本文轉自Saylor87 51CTO部落格,原文連結:http://blog.51cto.com/skynet/365552,如需轉載請自行聯系原作者