现在的位置: 首页 > 综合 > 正文

通过自定义配置实现插件式设计

2011年11月15日 ⁄ 综合 ⁄ 共 5392字 ⁄ 字号 评论关闭

软件设计有一句话叫做“约定优于配置”,很多人将其作为拒绝配置的理由。但是,“约定”和“配置”的使用,都有个度的问题。我不赞为了所谓的扩展性,为你的应用设计一套只有你自己才能看懂的配置体系。但是,在很多场景中,配置是提供应用灵活度的首要甚至是唯一途径。对于框架的设计者来说,对于配置的驾驭是一项基本的技能。

可能你很少使用自定义配置,可能你理解的自定义配置仅仅限于AppSetting,不过我想你应该对于System.Configuration这个命名空间下的几个基本的类型有基本的了解。比如ConfigurationSectionConfigurationElementConfigurationElementCollection等。本篇文章不会介绍关于System.Configuration的基础知识,而是通过一个简单的例子为你讲述一些所谓“高级”的知识点,比如“不可识别配置元素的动态解析”。(源代码从这里下载)

目录
一、通过自定义配置实现的最终效果
二、相关配置类型的定义
三、两个重要的类型:NameTypeConfigurationElement和NameTypeConfigurationElementCollection<T>
四、ResourceProviderFactory的定义
五、补充

一、通过自定义配置实现的最终效果

为了让大家对自定义配置的作用有一个深刻的映像,我们先来给出一个简单的例子。我们采用在《.NET的资源并不限于.resx文件,你可以采用任意存储形式》中介绍的关于自定义ResourceManager以实现对多种资源存储形式的支持。现在只关注与资源的读取,我们将基于不同存储形式的资源读取操作实现在相应的ResourceProovider中,它们实现如下一个简单的IResourceProvider接口。

   1: public interface IResourceProvider

   2: {

   3:     object GetObject(string key);

   4: }

然后我们创建两个具体的ResourceProvider:DbResourceProviderXmlResourceProvider,它们分别基于数据库表和XML文件的资源存储形式。DbResourceProvider需要连接数据库,需要引用配置的连接字符串,所以有一个ConnectionStringName属性;而XmlResourceProvider需要访问具体的XML文件,FileName属性表示文件路径。

   1: [ConfigurationElementType(typeof(DbResourceProviderConfigurationElement))]

   2: public class DbResourceProvider : IResourceProvider

   3: {

   4:     public string ConnnectionStringName { get; private set; }

   5:     public DbResourceProvider(string connectionStringName)

   6:     {

   7:         this.ConnnectionStringName = connectionStringName;

   8:     }

   9:     public object GetObject(string key)

  10:     {

  11:         throw new NotImplementedException();

  12:     }

  13:     public override string ToString()

  14:     {

  15:         return string.Format("{0}\n\tConncectionString Name:{1}", typeof(DbResourceProvider).FullName, this.ConnnectionStringName);

  16:     }

  17: }

  18:  

  19: [ConfigurationElementType(typeof(XmlResourceProviderConfigurationElement))]

  20: public class XmlResourceProvider : IResourceProvider

  21: {

  22:     public string FileName { get; private set; }

  23:     public XmlResourceProvider(string fileName)

  24:     {

  25:         this.FileName = fileName;

  26:     }

  27:     public object GetObject(string key)

  28:     {

  29:         throw new NotImplementedException();

  30:     }

  31:     public override string ToString()

  32:     {

  33:         return string.Format("{0}\n\tFile Name:{1}", typeof(XmlResourceProvider).FullName, this.FileName);

  34:     }

  35: }

具体使用哪个ResourceProvider,通过配置来决定。整个配置定义在<artech.resources>配置节中,该配置节具有一个<providers>子节点,它定义了一系列ResourceProvider的列表。每个ResourceProvider配置具有两个相同的属性:Name和Type,以及一些自己专属的配置属性(比如DbResourceProvider的connectionStringName,XmlResourceProvider的fileName)。至于默认采用哪个Provider,则通过配置节的defaultProvider属性来决定。在本例中,我们默认采用的是DbProvider。

   1: <?xml version="1.0" encoding="utf-8" ?>

   2: <configuration>

   3:   <configSections>

   4:     <section name="artech.resources" type="Artech.Resources.Configuration.ResourceSettings,Artech.CustomConfiguration"/>

   5:   </configSections>

   6:   <artech.resources defaultProvider="DbProvider">

   7:     <providers>

   8:       <add name="DbProvider" type="Artech.Resources.DbResourceProvider, Artech.CustomConfiguration" connectionStringName="LocalSqlServer"/>

   9:       <add name="XmlProvider" type="Artech.Resources.XmlResourceProvider, Artech.CustomConfiguration" fileName="C:\resources.xml"/>

  10:     </providers>

  11:   </artech.resources>

  12: </configuration>

现在我们有一个ResourceProviderFactory的工厂类来帮助我们根据配置创建默认的ResourceProvider,或者创建指定名称的ResourceProvider。现在我们按照如下的方式使用ResourceProviderFactory。

   1: static void Main(string[] args)

   2: {

   3:     IResourceProvider resourceProvider = ResourceProviderFactory.GetResourceProvider();

   4:     Console.WriteLine(resourceProvider);

   5:     Console.WriteLine();

   6:  

   7:     resourceProvider = ResourceProviderFactory.GetResourceProvider("XmlProvider");

   8:     Console.WriteLine(resourceProvider);

   9:     Console.WriteLine();

  10:  

  11:     resourceProvider = ResourceProviderFactory.GetResourceProvider("DbProvider");

  12:     Console.WriteLine(resourceProvider);

  13:     Console.WriteLine();

  14: }

输出结果:

   1: Artech.Resources.DbResourceProvider

   2:         ConncectionString Name:LocalSqlServer

   3:  

   4: Artech.Resources.XmlResourceProvider

   5:         File Name:C:\resources.xml

   6:  

   7: Artech.Resources.DbResourceProvider

   8:         ConncectionString Name:LocalSqlServer

接下来我们就来介绍整个配置体系,以及ResourceProviderFactory的实现。

二、相关配置类型的定义

我们现在来看看与配置相关的类型的定义。整个配置节定义在如下一个ResourceSettings的类中,它直接继承自ConfigurationSection。ResourceSettings具有两个配置属性:DefaultProvider和Providers,分别代表<artech.resources>的defaultProvider属性和<providers>子节点。

   1: public class ResourceSettings: ConfigurationSection

   2: {

   3:     [ConfigurationProperty("defaultProvider", IsRequired = true)]

   4:     public string DefaultProvider

   5:     {

   6:         get{return (string)this["defaultProvider"];}

   7:         set{this["defaultProvider"] = value;}

   8:     }

   9:     [ConfigurationProperty("providers", IsRequired = true)]

  10:     public NameTypeElementCollection<ResourceProviderConfigurationElement> Providers

  11:     {

  12:         get{return (NameTypeElementCollection<ResourceProviderConfigurationElement>)this["providers"];}

  13:         set{this["providers"] = value;}

  14:     }

  15:     public static ResourceSettings GetConfiguration()

  16:     {

  17:         return (ResourceSettings)ConfigurationManager.GetSection("artech.resources");

  18:     }

  19: }

属性Providers是一个名称为NameTypeElementCollection<T>的泛型类型。从名称我们不难看出,这是一个集合类型,代表配置的ResourceProvider集合。而基于ResourceProvider的配置定义在如下一个ResourceProviderConfigurationElement抽象类中。该类继承自我们自定义的NameTypeConfigurationElement类型,具有一个CreateProvider抽象方法用于创建相应的ResourceProvider。

   1: public abstract class ResourceProviderConfigurationElement: NameTypeConfigurationElement

   2: {

   3:     public abstract IResourceProvider CreateProvider();

   4: }

DbResourceProvider和XmlResourceProvider具有各自的ResourceProviderConfigurationElement,分别为DbResourceProviderConfigurationElementXmlResourceProviderConfigurationElement

   1: public class DbResourceProviderConfigurationElement : ResourceProviderConfigurationElement

   2: {

   3:     [ConfigurationProperty("connectionStringName", IsRequired = true)]

   4:     public string ConnectionStringName

   5:     {


抱歉!评论已关闭.