概述
配置應用程序塊為應用系統提供了一個通用的配置管理解決方案,可以方便的從各種存儲中讀取配置信息。在設計上旨在提供一個用於讀/寫配置數據的簡單接口,實現配置數據的讀寫與數據的存儲相分離。使用Storage Provider 和 Transformers在應用和物理存儲之間傳遞數據,同時采用抽象AbstractFactory模式生成Provider數據。先解釋一下配置應用程序塊中用到的兩個重要的概念:
Storage Provider是讀寫某個物理存儲的對象,比如XML文件或SQL數據庫
Transformers是在存儲格式和應用格式之間轉換配置數據的對象
結構設計
下圖展示了組成配置應用程序塊的類和對象之間的關系。該圖假定您使用 XML 文件存儲提供程序和轉換器,它們包含在應用程序塊中。XML 文件存儲提供程序以文件的形式存儲配置數據。(其他提供程序使用其他形式的存儲,例如 Windows 注冊表。)XmlFileStorageProvider 對象指向一個包含特定配置節的配置設置的文件。ConfigurationBuilder 對象指向一個包含特定配置節的配置元數據的文件。通常,包含配置元數據的文件名為 App.config(對於基於 Windows 的應用程序)或 Web.config(對於基於 Web 的應用程序)。
配置應用程序塊將配置元數據和實際的配置設置分隔開來。應用程序塊將元數據放在它自己的文件中,而該文件獨立於存儲配置設置的位置。配置設置經過分組並稱為配置節。應用程序使用的每個企業程序庫應用程序塊都有其自己的配置節,該配置節存儲在其自己的文件中。配置應用程序塊使用配置元數據來訪問配置中的數據。
元數據指向配置存儲位置並包含一些信息,例如,配置應用程序塊讀/寫配置數據所需的轉換器和存儲提供程序的類型。配置元數據文件被分成節。每一節都包含在配置存儲位置讀/寫一組特定的配置設置所需的信息。下圖展示了元數據和配置區之間的關系:
ConfigurationManager 類提供一個在所定義的存儲位置讀/寫特定配置節的配置設置的靜態外觀(個人覺得是在這裡運用了門面模式,不知道對不對?)。ConfigurationManager 對象從應用程序域配置文件讀取配置元數據,然後使用這些信息來讀/寫配置節信息。
ConfigurationManager 類的靜態方法使用 ConfigurationBuilder 對象的實例。ConfigurationBuilder 可創建文件存儲提供程序和轉換器對象。這些對象可管理配置數據和元數據。
IStorageProviderReader 接口定義了用於從存儲位置讀取配置信息的接口。IStorageProviderWriter 接口實現了 IStorageProviderReader 接口,還定義了用於寫入配置信息的接口。配置應用程序塊包含一個支持該接口的提供程序 XmlFileStorageProvider,它在一個 XML 文件中讀/寫配置數據。
ITransformer 接口可轉換應用程序和存儲提供程序之間的配置設置對象。配置應用程序塊包含一個實現該接口的提供程序,即 XmlSerializerTransformer 類。XmlSerializerTransformer 類實現了應用程序定義的運行時對象和 XmlNode 對象之間的轉換,而無需應用程序來配置轉換器。如果沒有轉換器,配置設置對象就會以存儲提供程序提供的相同格式返回到應用程序。
每個配置節的設置都緩存在一個哈希表中。當客戶端請求配置數據時,ConfigurationBuilder 對象會在緩存中查找數據。如果在緩存中找到配置數據,ConfigurationBuilder 對象就不必訪問存儲中的配置數據。如果文件存儲提供程序檢測到存儲中的配置數據已經更改,則 ConfigurationBuilder 對象就會清除緩存。ConfigurationManager 對象允許應用程序清除全部緩存,或者只清除給定節名的緩存。如果清除了緩存,則下一個讀取操作就會訪問存儲位置中的配置設置。
解耦這個詞在配置應用程序塊中得到了很好的體現,將配置數據的讀寫和配置數據的存儲分離。在配置應用程序塊中已經實現了讀寫XML的Storage Provider,同時支持開發者根據數據存儲的物理位置來編寫相應的Provider,見下圖:
如果我們編寫了自己的Storage Provider和 Transformer,那麼我們可以很簡單的利用配置工具來修改數據的存儲而無須修改任何代碼(這也是整個企業庫的設計思想的體現,配置驅動)。簡化配置
配置應用程序塊做到讓開發人員通過一行代碼來實現對配置數據的讀取和寫入,下面的代碼展示了如何讀取和寫入配置數據:
1/**//// 讀取配置數據
2MyConfigClass configData = ConfigurationManager.GetConfiguration("MySettings") as MyConfigClass;
3
4/**//// 寫入配置數據
5ConfigurationManager.WriteConfiguration("MySettings", configData);
而應用程序塊在讀寫配置數據時,實際上是執行了ConfigurationBuilder的ReadConfiguration()和WriteConfiguration()方法。ConfigurationManager類通過外觀模式把這個兩個方法封裝成了上面所寫的GetConfiguration()和WriteConfiguration()方法。
下面我們看一下具體的讀寫代碼:
1public object ReadConfiguration(string sectionName)
2{
3 /**////驗證有效性
4 ValidateSection(sectionName);
5
6 /**////變量configurationSection代表具體的配置數據類:MyConfigClass
7 object configurationSection = sections.GetSection(sectionName);
8
9 /**////緩存存在就直接返回結果
10 if (IsConfigurationSectionCached(configurationSection))
11 {
12 return configurationSection;
13 }
14
15 IStorageProviderReader storageProviderReader = CreateStorageProvider(sectionName);
16
17 /**////變量configurationSettings代表的是具體配置數據中的配置項物理格式的數據
18 ///核心功能,調用Read()方法,實際的讀取由Provider完成
19 object configurationSettings = storageProviderReader.Read();
20 if (configurationSettings == null)
21 {
22 return null;
23 }
24
25 ITransformer transformer = CreateTransformer(sectionName);
26 if (transformer != null)
27 {
28 /**////將配置數據由代表物理格式配置數據的類轉變為代表應用程序直接訪問的配置類
29 configurationSection = transformer.Deserialize(configurationSettings);
30 }
31 else
32 {
33 configurationSection = configurationSettings;
34 }
35
36 ConfigurationChangedEventHandler changed = new ConfigurationChangedEventHandler(OnExternalConfigurationChanged);
37
38 /**////增加到緩存中
39 sections.AddSection(sectionName, configurationSection, changed, storageProviderReader);
40
41 return configurationSection;
42}
1public void WriteConfiguration(string sectionName, object configValue)
2{
3 /**////驗證有效性
4 ValidateSection(sectionName);
5
6 /**////注冊寫前事件
7 ConfigurationChangingEventArgs args = CreateConfigurationChangingEventArgs(sectionName, configValue);
8 OnConfigurationChanging(args);
9 if (!args.Cancel)
10 {
11 /**////創建編寫器
12 IStorageProviderWriter configStorageWriter = GetConfigurationStorageWriter(sectionName);
13
14 /**////將要保存的值轉換成Provider可識別的格式,具體何種格式是由配置元數據決定的
15 object writeData = GetSerializedDataToWrite(sectionName, configValue);
16 ConfigurationWriterActionCommand writerActionCommand = new ConfigurationWriterActionCommand(configStorageWriter, writeData);
17
18 /**////如果配置節尚不存在就添加此配置數據
19 if (!sections.ContainsSection(sectionName))
20 {
21 AddSection(sectionName, configValue, configStorageWriter);
22 }
23
24 /**////如果配置節已存在就更新此配置數據
25 sections.UpdateSection(sectionName, writerActionCommand, configValue);
26
27 /**////注冊寫完成事件
28 ConfigurationChangedEventArgs changedArgs = new ConfigurationChangedEventArgs(configFile.FileName, sectionName);
29 OnConfigurationChanged(changedArgs);
30 }
31}
擴展器和工廠
由於找不到更好的中文字來說明Provider,所以只好用了擴展器這個名字,大家見諒。來看一下配置應用程序塊中的Providers結構圖:
IConfigurationProvider 接口是所有的Providers必須實現的,以便配置應用程序塊能夠創建和初始化它們。該接口中有一個方法Initialize()和一個屬性ConfigurationName,配置應用程序塊調用Initialize()方法來創建每一個Providers。配置應用程序塊中包含了一個抽象的基類ConfigurationProvider。它實現了IConfigurationProvider 接口中的ConfigurationName屬性。配置應用程序塊中的Factories結構圖:
ConfigurationFactory是一個抽象的基類,它定義了應用程序塊中所有的工廠類的接口,所有的Factory類必須從它繼承。ProviderFactory類實現了IConfigurationProvider並從ConfigurationFactory類繼承,也是一個抽象類。總結好了,這裡引用MSDN上的一句話來結束這篇Post,“設計了配置應用程序塊,您就可以用最適合應用程序要求的方式將配置數據存儲在應用程序中,使您不受存儲方法的限制”。