Enterprise Library 自定义应用程序块实战(下)

1. 概述
上篇,我对 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 类用于注册各节点可以创建的子节点的右键菜单命令,在此不再详述。

5 配置控制台的执行注册
配置控制台的注册包括二个方面:注册 UI 、配置的序列化和反序列化,这是配置控制台和自定义配置的接口,上面所有的类都会通过此接口被配置控制台直接和间接的使用。
在 此将这个注册类命名为 PlugInLoaderConfigurationDesignManager ,PlugInLoaderConfigurationDesignManager 类继承自 ConfigurationDesignManager 类。在此需要重写其中的三个方法:Register()、ConfigurationSectionInfo() 和 OpenCore() 。
其中:
  • Register() 方法注册 UI 命令;
  • ConfigurationSectionInfo() 方法用于将节点序列化为 XML 配置文件;
  • OpenCore() 方法用户将配置文件反序列化为 UI 上的配置节点。
配置控制台就是通过上面三个方法来完成配置的打开、修改和保存的。

最后,打开项目的 AssemblyInfo.cs 文件,在其中加上以下语句:

[assembly: ConfigurationDesignManager(typeof(PlugInLoaderConfigurationDesignManager))]

它告诉配置控制台:“我是一个用于可视化配置的程序集,加载我吧!!!”,如果没有这行语句,配置控制台将会对此程序集视而不见。

6 小结
在完成上面各步骤后,我们的应用程序块就可以使用 Enterprise Library 配置控制台来进行可视化配置了。
讨论和代码下载请到:http://forum.entlib.net.cn/yaf_postst38_Enterprise-Library-.aspx
posted @ 2007-12-01 22:49  Dorian Deng  阅读(1843)  评论(3编辑  收藏  举报