Enterprise Library 自定义应用程序块实战(下)
1. 概述
5 配置控制台的执行注册
上篇,我对 Enterprise Library 自定义应用程序块的运行时编写进行了描述,在此我们将学习如何编写自定义应用程序块的设计时,以支持 Enterprise Library 配置控制台的使用。
编写设计时包括四个部分:定义配置所对应的节点类、配置节点与配置文件 XML 间的序列化和反序列化类、Enterprise Library 配置控制台 UI 注册类和配置控制台的执行注册。
2. 定义配置节点编写设计时包括四个部分:定义配置所对应的节点类、配置节点与配置文件 XML 间的序列化和反序列化类、Enterprise Library 配置控制台 UI 注册类和配置控制台的执行注册。
由于应用程序配置文件是基于 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 间的序列化和反序列化根据应用程序块的源 Scheme ,我们可以得出下列的对应关系,其中第一列为配置元素,第二列为配置类,第三列为配置节点类:
<plugInConfiguration> --> PlugInLoaderSettings --> PlugInLoaderSettingsNode
<plugInProviders> --> PlugInLoaderSettings.PlugInProviders --> PlugInProviderCollectionNode
<add> --> PlugInProviderData --> PlugInProviderNode
以上三个配置结点类都继承自 Microsoft.Practices.EnterpriseLibrary.Configuration.Design 命名空间中的 ConfigurationNode 类。
另 外,针对在上篇中所实现的二个具体提供程序的配置类:InjectionPlugInProviderData 和 NaivePlugInProviderData ,也实现了继承自 PlugInProviderNode 的相应的配置节点类 InjectionPlugInProviderNode 和 NaivePlugInProviderNode 。
因为这些配置节点类仅是普通的设计时组件类,所以在此不再详述。
在配置控制台中修改节点的信息后,必须通过序列化成 XML 才能保存到配置文件中,同样,配置文件中的 XML 配置数据必须通过反序列化才能形成 UI 上的节点。
在 此,序列化我们可以通过一个 PlugInLoaderSettingsBuilder 类即可实现,而反序列化需要 PlugInLoaderSettingsNode 所对应的 PlugInLoaderSettingsNodeBuilder 和 PlugInProviderCollectionNode 所对应的 PlugInProviderCollectionNodeBuilder 类来实现,其中反序化的二个类继承中 Microsoft.Practices.EnterpriseLibrary.Configuration.Design 命名空间中的 NodeBuilder 类。
3.1 PlugInLoaderSettingsBuilder 类在 此,序列化我们可以通过一个 PlugInLoaderSettingsBuilder 类即可实现,而反序列化需要 PlugInLoaderSettingsNode 所对应的 PlugInLoaderSettingsNodeBuilder 和 PlugInProviderCollectionNode 所对应的 PlugInProviderCollectionNodeBuilder 类来实现,其中反序化的二个类继承中 Microsoft.Practices.EnterpriseLibrary.Configuration.Design 命名空间中的 NodeBuilder 类。
这是 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 类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 来完成序列化。
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 注册类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() 方法用于记录节点映射错误。
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 类在此我们关注一下其中的一个私有方法: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 配置节点。
PlugInLoaderNodeMapRegistrar
类继承自 NodeMapRegistrar 类,主要重写了其 Register ,用于注册二个提供程序子类
NaivePlugInProvider 和 InjectionPlugInProvider
类的配置数据类和配置节点的映射,
PlugInProviderCollectionNodeBuilder.CreatePlugInProviderNode() 方法中的语句:
PlugInProviderNode pipNode = this.NodeCreationService.CreateNodeByDataType( pipData.GetType(), new object[] { pipData }) as PlugInProviderNode;
中的方法 CreateNodeByDataType() 就是使用节点映射来创建节点的。
4.3 PlugInLoaderCommandRegistrar 类PlugInProviderNode pipNode = this.NodeCreationService.CreateNodeByDataType( pipData.GetType(), new object[] { pipData }) as PlugInProviderNode;
中的方法 CreateNodeByDataType() 就是使用节点映射来创建节点的。
PlugInLoaderCommandRegistrar 类用于注册各节点可以创建的子节点的右键菜单命令,在此不再详述。
配置控制台的注册包括二个方面:注册 UI 、配置的序列化和反序列化,这是配置控制台和自定义配置的接口,上面所有的类都会通过此接口被配置控制台直接和间接的使用。
在 此将这个注册类命名为 PlugInLoaderConfigurationDesignManager ,PlugInLoaderConfigurationDesignManager 类继承自 ConfigurationDesignManager 类。在此需要重写其中的三个方法:Register()、ConfigurationSectionInfo() 和 OpenCore() 。
其中:
最后,打开项目的 AssemblyInfo.cs 文件,在其中加上以下语句:
[assembly: ConfigurationDesignManager(typeof(PlugInLoaderConfigurationDesignManager))]
它告诉配置控制台:“我是一个用于可视化配置的程序集,加载我吧!!!”,如果没有这行语句,配置控制台将会对此程序集视而不见。
6 小结在 此将这个注册类命名为 PlugInLoaderConfigurationDesignManager ,PlugInLoaderConfigurationDesignManager 类继承自 ConfigurationDesignManager 类。在此需要重写其中的三个方法:Register()、ConfigurationSectionInfo() 和 OpenCore() 。
其中:
- Register() 方法注册 UI 命令;
- ConfigurationSectionInfo() 方法用于将节点序列化为 XML 配置文件;
- OpenCore() 方法用户将配置文件反序列化为 UI 上的配置节点。
最后,打开项目的 AssemblyInfo.cs 文件,在其中加上以下语句:
[assembly: ConfigurationDesignManager(typeof(PlugInLoaderConfigurationDesignManager))]
它告诉配置控制台:“我是一个用于可视化配置的程序集,加载我吧!!!”,如果没有这行语句,配置控制台将会对此程序集视而不见。
在完成上面各步骤后,我们的应用程序块就可以使用 Enterprise Library 配置控制台来进行可视化配置了。
讨论和代码下载请到:http://forum.entlib.net.cn/yaf_postst38_Enterprise-Library-.aspx。
讨论和代码下载请到:http://forum.entlib.net.cn/yaf_postst38_Enterprise-Library-.aspx。