Enterprise Library Step By Step系列(十五):配置应用程序块——设计篇
Terrylee
,
2005
年
12
月
05
日
概述
配置应用程序块为应用系统提供了一个通用的配置管理解决方案,可以方便的从各种存储中读取配置信息。在设计上旨在提供一个用于读
/
写配置数据的简单接口,实现配置数据的读写与数据的存储相分离。使用
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
/// 读取配置数据
2 MyConfigClass configData = ConfigurationManager.GetConfiguration( " MySettings " ) as MyConfigClass;
3
4 /// 写入配置数据
5 ConfigurationManager.WriteConfiguration( " MySettings " , configData);
2 MyConfigClass configData = ConfigurationManager.GetConfiguration( " MySettings " ) as MyConfigClass;
3
4 /// 写入配置数据
5 ConfigurationManager.WriteConfiguration( " MySettings " , configData);
而应用程序块在读写配置数据时,实际上是执行了
ConfigurationBuilder
的
ReadConfiguration()
和
WriteConfiguration()
方法。
ConfigurationManager
类通过外观模式把这个两个方法封装成了上面所写的
GetConfiguration()
和
WriteConfiguration()
方法。
下面我们看一下具体的读写代码:
1
public
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}
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}
1
public
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}
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
类继承,也是一个抽象类。
总结
好了,这里引用
M
SD
N
上的一句话来结束这篇
Post
,“
设计了配置应用程序块,您就可以用最适合应用程序要求的方式将配置数据存储在应用程序中,使您不受存储方法的限制
”。
本文转自lihuijun51CTO博客,原文链接:
http://blog.51cto.com/terrylee/67617
,如需转载请自行联系原作者