Enterprise Library Step By Step系列(十五):配置应用程序块——设计篇

Enterprise Library Step By Step系列(十五):配置应用程序块——设计篇

Terrylee20051205

概述

配置应用程序块为应用系统提供了一个通用的配置管理解决方案,可以方便的从各种存储中读取配置信息。在设计上旨在提供一个用于读/写配置数据的简单接口,实现配置数据的读写与数据的存储相分离。使用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 对象允许应用程序清除全部缓存,或者只清除给定节名的缓存。如果清除了缓存,则下一个读取操作就会访问存储位置中的配置设置。

 

解耦

这个词在配置应用程序块中得到了很好的体现,将配置数据的读写和配置数据的存储分离。在配置应用程序块中已经实现了读写XMLStorage Provider,同时支持开发者根据数据存储的物理位置来编写相应的Provider,见下图:

 

如果我们编写了自己的Storage Provider Transformer,那么我们可以很简单的利用配置工具来修改数据的存储而无须修改任何代码(这也是整个企业库的设计思想的体现,配置驱动)。

 

简化配置

配置应用程序块做到让开发人员通过一行代码来实现对配置数据的读取和写入,下面的代码展示了如何读取和写入配置数据:

1/// 读取配置数据
2MyConfigClass configData = ConfigurationManager.GetConfiguration("MySettings"as MyConfigClass;
3
4/// 写入配置数据
5ConfigurationManager.WriteConfiguration("MySettings", configData);

而应用程序块在读写配置数据时,实际上是执行了ConfigurationBuilderReadConfiguration()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,“设计了配置应用程序块,您就可以用最适合应用程序要求的方式将配置数据存储在应用程序中,使您不受存储方法的限制”。

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。

posted on 2005-12-06 09:06 TerryLee 阅读(4109) 评论(20)  编辑 收藏 所属分类: Enterprise Library

评论

#1楼  2005-12-06 10:04 goodbaby      

好   回复  引用  查看    

#2楼  2005-12-06 10:48 高海东      

不错,辛苦了   回复  引用  查看    

#3楼 [楼主] 2005-12-06 11:03 Terrylee      

@goodbaby,高海东

多谢两位的支持

由于我也是第一次研究EL的类设计

如果有理解不正确的地方希望能提出来

大家共同提高,争取最后写我们自己的Application Block

呵呵,有点做梦了^_^   回复  引用  查看    

#4楼  2005-12-06 11:35       

好,一直在关注。
大家一起 来 快乐的学习 EL的类设计 .
辛苦了。   回复  引用  查看    

#5楼 [楼主] 2005-12-06 11:51 Terrylee      

@杰

谢谢你长期以来对本系列随笔的关注

这次很高兴Enterprise Library Step By Step系列随笔入选“刊登在《程序员2005合订本》上的2005年度博客园50篇精华文章”

离不开大家的支持^_^   回复  引用  查看    

#6楼  2005-12-06 12:30 工作、生活 [未注册用户]

我当时也写了一个,就是太乱了。
http://lifework.cnblogs.com/archive/2005/08/03/207039.html

写个 制作自己的应用程序块 的文档,一直没发上来。   回复  引用    

#7楼  2005-12-06 12:59 木野狐      

"Provider" 一般翻译为 “提供者”比较正规。   回复  引用  查看    

#8楼 [楼主] 2005-12-06 15:08 Terrylee      

@木野狐

我也看到过很多文章里面都翻译成“提供者”

我当时翻译为“扩展器”是因为我觉得在企业库里面Provider是为了扩展而存在的,所以就这样翻译了^_^   回复  引用  查看    

#9楼  2005-12-07 10:09 过客来的 [未注册用户]

在企业库2.0版本中,配置模块和安全模块有很大的变化。
安全模块基本上采用了net 2.0中内置的API。
配置模块则采用net 2.0中内置的system.Configuration.   回复  引用    

#10楼  2006-01-16 13:28 avril2005 [未注册用户]

如果我想实现每一个用户有自己的配置项,如页面的风格,Storage Provider用数据库实现,这样与直接用DataAccess Application Block,读写配置数据有什么不同呢?
赐教!   回复  引用    

#11楼 [楼主] 2006-01-16 14:07 Terrylee      

@avril2005

这两者完全是两码事,Storage Provider是提供了你配置数据的存储,而DAAB是提供了统一的访问数据库的方法。

至于要实现每一个用户有自己的配置项,你可以去参考一下安全应用程序块中的个性化管理部分的内容,也许会对你有所帮助!   回复  引用  查看    

#12楼  2007-11-29 18:20 RyanYu      

辛苦了   回复  引用  查看    


标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2005-12-06 09:13 编辑过
 
另存  打印
 





导航

公告

  • 网名:TerryLee
  • 本名:李会军
  • 位置:中国北京 Ethos
  • 联系方式:
  • 访问我的个人主页

 MVP配置

 版权声明

  • 本站采用创作共用许可 署名,非商业

绿色通道

IT新闻

统计

与我联系

留言簿(322)

我的标签

随笔分类

随笔档案

个人站点

关注项目