上篇,我对 Enterprise Library 自定义应用程序块的运行时编写进行了描述,在此我们将学习如何编写自定义应用程序块的设计时,以支持 Enterprise Library 配置控制台的使用。 编写设计时包括四个部分:定义配置所对应的节点类、配置节点与配置文件 XML 间的序列化和反序列化类、Enterprise Library 配置控制台 UI 注册类和配置控制台的执行注册。
2. 定义配置节点 由于应用程序配置文件是基于 XML 的,因此它们天生便具有层次结构。配置控制台将此模型化为一个由节点组成的树。运行时组件中的每个配置元素必须由一个设计时节点类来表示,该节点类为配置类提供了额外的设计时行为。 根据应用程序块的源 Scheme ,我们可以得出下列的对应关系,其中第一列为配置元素,第二列为配置类,第三列为配置节点类: <plugInConfiguration> --> PlugInLoaderSettings --> PlugInLoaderSettingsNode <plugInProviders> --> PlugInLoaderSettings.PlugInProviders --> PlugInProviderCollectionNode <add> --> PlugInProviderData --> PlugInProviderNode 以上三个配置结点类都继承自 Microsoft.Practices.EnterpriseLibrary.Configuration.Design 命名空间中的 ConfigurationNode 类。 另 外,针对在上篇中所实现的二个具体提供程序的配置类:InjectionPlugInProviderData 和 NaivePlugInProviderData ,也实现了继承自 PlugInProviderNode 的相应的配置节点类 InjectionPlugInProviderNode 和 NaivePlugInProviderNode 。 因为这些配置节点类仅是普通的设计时组件类,所以在此不再详述。
3. 配置节点与配置文件 XML 间的序列化和反序列化 在配置控制台中修改节点的信息后,必须通过序列化成 XML 才能保存到配置文件中,同样,配置文件中的 XML 配置数据必须通过反序列化才能形成 UI 上的节点。 在 此,序列化我们可以通过一个 PlugInLoaderSettingsBuilder 类即可实现,而反序列化需要 PlugInLoaderSettingsNode 所对应的 PlugInLoaderSettingsNodeBuilder 和 PlugInProviderCollectionNode 所对应的 PlugInProviderCollectionNodeBuilder 类来实现,其中反序化的二个类继承中 Microsoft.Practices.EnterpriseLibrary.Configuration.Design 命名空间中的 NodeBuilder 类。
3.1 PlugInLoaderSettingsBuilder 类 这是 Enterprise Library 配置控制台用于序列化配置数据为 XML 的类,代码如下: using Microsoft.Practices.EnterpriseLibrary.Configuration.Design; using System; namespace PlugInLoader.Configuration.Design { internal class PlugInLoaderSettingsBuilder { private PlugInLoaderSettingsNode settingsNode_; private IConfigurationUIHierarchy hierarchy_; internal PlugInLoaderSettingsBuilder( IServiceProvider serviceProvider, PlugInLoaderSettingsNode settingsNode) { this.settingsNode_ = settingsNode; this.hierarchy_ = ServiceHelper.GetCurrentHierarchy(serviceProvider); } internal PlugInLoaderSettings Build() { PlugInLoaderSettings settings = new PlugInLoaderSettings(); this.BuildPlugInProviders(settings); return settings; } private void BuildPlugInProviders(PlugInLoaderSettings settings) { PlugInProviderCollectionNode providersNode = this.hierarchy_.FindNodeByType(this.settingsNode_, typeof(PlugInProviderCollectionNode)) as PlugInProviderCollectionNode; if (providersNode != null) { foreach (PlugInProviderNode pipNode in providersNode.Nodes) { settings.PlugInProviders.Add(pipNode.PlugInProviderData); } } } } } 在 此代码中,我们需要注意一个重要的成员变量:IConfigurationUIHierarchy hierarchy_,此变量表示了 Enterprise Library 配置控制台 UI 节点的层次信息,它在构造函数中通过 ServiceHelper.GetCurrentHierarchy 方法获得,然后,在序列化方法 BuildPlugInProviders() 中通过 hierarchy_.FindNodeByType 方法来从层次信息中获取节点的信息以构建相应的 PlugInLoaderSettings 来完成序列化。
3.2 PlugInLoaderSettingsNodeBuilder 类 PlugInLoaderSettingsNodeBuilder 类主要通过使用 PlugInProviderCollectionNodeBuilder 来完成反序列化,在此不再解释,请参见源码。
3.3 PlugInProviderCollectionNodeBuilder 类 PlugInProviderCollectionNodeBuilder 类用于反序列化 PlugInLoaderSettings.PlugInProviders ,代码如下: using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; using Microsoft.Practices.EnterpriseLibrary.Configuration.Design; using System; namespace PlugInLoader.Configuration.Design { internal class PlugInProviderCollectionNodeBuilder : NodeBuilder { private NameTypeConfigurationElementCollection<PlugInProviderData, PlugInProviderData> plugInProviders_; private PlugInProviderCollectionNode node_; internal PlugInProviderCollectionNodeBuilder( IServiceProvider serviceProvider, NameTypeConfigurationElementCollection<PlugInProviderData, PlugInProviderData> plugInProviders) : base(serviceProvider) { this.plugInProviders_ = plugInProviders; } internal PlugInProviderCollectionNode Build() { this.node_ = new PlugInProviderCollectionNode(); this.plugInProviders_.ForEach(new Action<PlugInProviderData>( this.CreatePlugInProviderNode)); return this.node_; } private void CreatePlugInProviderNode(PlugInProviderData pipData) { PlugInProviderNode pipNode = this.NodeCreationService.CreateNodeByDataType( pipData.GetType(), new object[] { pipData }) as PlugInProviderNode; if (pipNode == null) { this.LogNodeMapError(this.node_, pipData.GetType()); } this.node_.AddNode(pipNode); } } } 在此要注意的是 Build() 方法中的 plugInProviders_.ForEach() 方法,此方法对 NameTypeConfigurationElementCollection 中的每个元素执行指定的操作。 以 及 CreatePlugInProviderNode() 方法中的 this.NodeCreationService.CreateNodeByDataType() ,此方法完成真正的反序列化以生成节点。另一个 this.LogNodeMapError() 方法用于记录节点映射错误。
4. Enterprise Library 配置控制台 UI 注册类 UI 注册包括命令的注册和节点映射的注册,主要包括三个类:PlugInLoaderCommandRegistrar 类 、 PlugInLoaderNodeMapRegistrar 类和 PlugInLoaderCommandRegistrar 类。
4.1 PlugInLoaderCommandRegistrar 类 PlugInLoaderCommandRegistrar 类完成 UI 命令的注册,它继承自 Microsoft.Practices.EnterpriseLibrary.Configuration.Design 中的 CommandRegistrar 类,需要实现其 Register() 以完成注册。 在此我们关注一下其中的一个私有方法:AddPlugInLoaderCommand() ,其他方法请参见源码。AddPlugInLoaderCommand() 方法的代码如下: private void AddPlugInLoaderCommand() { ConfigurationUICommand cmd = ConfigurationUICommand.CreateSingleUICommand( this.ServiceProvider, "Plug-In Loader Application Block", "Add the Plug-In Loader Application Block", new AddPlugInLoaderSettingsNodeCommand(this.ServiceProvider), typeof(PlugInLoaderSettingsNode)); this.AddUICommand(cmd, typeof(ConfigurationApplicationNode)); this.AddDefaultCommands(typeof(PlugInLoaderSettingsNode)); } 在 此方法中,创建了一个 ConfigurationUICommand 类的实例,此类是 UI 的命令,它注册了一个在创建节点时需要执行的命令:AddPlugInLoaderSettingsNodeCommand 类。AddPlugInLoaderSettingsNodeCommand 类继承自 AddChildNodeCommand 类,并实现了方法 ExecuteCore() ,以完成在创建节点时的其他默认操作。在此实现的 ExecuteCore() 方法为应用程序块的根节点生成了一个 PlugInProviderCollectionNode 配置节点。
4.2 PlugInLoaderNodeMapRegistrar 类 PlugInLoaderNodeMapRegistrar 类继承自 NodeMapRegistrar 类,主要重写了其 Register ,用于注册二个提供程序子类 NaivePlugInProvider 和 InjectionPlugInProvider 类的配置数据类和配置节点的映射, PlugInProviderCollectionNodeBuilder.CreatePlugInProviderNode() 方法中的语句: PlugInProviderNode pipNode = this.NodeCreationService.CreateNodeByDataType( pipData.GetType(), new object[] { pipData }) as PlugInProviderNode; 中的方法 CreateNodeByDataType() 就是使用节点映射来创建节点的。
4.3 PlugInLoaderCommandRegistrar 类 PlugInLoaderCommandRegistrar 类用于注册各节点可以创建的子节点的右键菜单命令,在此不再详述。
配置控制台的注册包括二个方面:注册 UI 、配置的序列化和反序列化,这是配置控制台和自定义配置的接口,上面所有的类都会通过此接口被配置控制台直接和间接的使用。 在 此将这个注册类命名为 PlugInLoaderConfigurationDesignManager ,PlugInLoaderConfigurationDesignManager 类继承自 ConfigurationDesignManager 类。在此需要重写其中的三个方法:Register()、ConfigurationSectionInfo() 和 OpenCore() 。 其中:
6 小结 - Register() 方法注册 UI 命令;
- ConfigurationSectionInfo() 方法用于将节点序列化为 XML 配置文件;
- OpenCore() 方法用户将配置文件反序列化为 UI 上的配置节点。
在完成上面各步骤后,我们的应用程序块就可以使用 Enterprise Library 配置控制台来进行可视化配置了。 讨论和代码下载请到: 。