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。