在自定义管道组件中更改消息的命名空间





5.00/5 (1投票)
如何在自定义管道组件中更改消息的命名空间
引言
本文详细介绍如何在 BizTalk 2006 R2 中开发自定义管道组件以更改消息的命名空间。在 BizTalk 2009 中,已有组件用于添加和删除命名空间。
此组件可在接收管道的解码阶段使用。一个 `public` 属性允许用户键入将添加到根节点的新命名空间,或替换 XML 消息中现有的命名空间。
代码
此项目在 Visual Studio 中创建为新的类库,并使用强名称密钥文件进行签名。
添加了一个引用,Microsoft.BizTalk.Pipeline.dll。可以在 BTS 安装位置的“Microsoft BizTalk Server 2006”文件夹中找到它。
此项目中的类重命名为 `ChangeNsDecoder`,代码首先添加以下命名空间
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
在下面的代码中,我向类添加了两个属性。第一个属性表示这是一个管道组件,第二个属性将组件的使用限制在管道的解码阶段。
管道组件实现的特定接口使其与其他组件区分开来。一旦组件实现了接口,就可以被 BizTalk 运行时调用。所有组件和组件类别都在 `Microsoft.BizTalk.Component.Interop` 命名空间中。解码管道组件需要实现 `IBaseComponent`、`IComponentUI` 和 `IComponent` 接口。此外,下面的类实现了 `IPersistPropertyBag` 接口,但这并非必需。
namespace Stm.ChangeNsDecoder
{
[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[ComponentCategory(CategoryTypes.CATID_Decoder)]
[System.Runtime.InteropServices.Guid("5136B07D-DC44-4481-B3A8-35DA712CC538")]
public class ChangeNsDecoder : IBaseComponent, IComponentUI,
IComponent, IPersistPropertyBag
{
在类内部,我实现了所有接口所需的属性和方法。
我从 IBaseComponent
接口开始。该接口提供了有关组件的基本信息的属性。
#region IBaseComponent
private const string _description =
"Pipeline component used to change the namespace of a message";
private const string _name = "ChangeNsDecoder";
private const string _version = "1.0.0.0";
// Component description
public string Description
{
get { return _description; }
}
// Component name
public string Name
{
get { return _name; }
}
// Component version
public string Version
{
get { return _version; }
}
#endregion
IComponentUI
接口定义了在管道设计器环境中使用的方法和属性。为保持简单,我在此处未提供任何代码。
#region IComponentUI
private IntPtr _icon = new IntPtr();
// Icon associated with this component
public IntPtr Icon
{
get { return _icon; }
}
// Verifies that all the configuration properties are set correctly
public System.Collections.IEnumerator Validate(object projectSystem)
{
return null;
}
#endregion
接下来,我实现了 `IPersistPropertyBag` 接口。这样做是为了存储管道组件的属性信息(在本例中是新命名空间)。当在接收管道中使用此组件时,它允许用户在 BizTalk 管理控制台的接收位置管道配置中的属性中键入新命名空间。
#region IPersistPropertyBag
private string _newNamespace;
public string NewNamespace
{
get { return _newNamespace; }
set { _newNamespace = value; }
}
public void GetClassID(out Guid classID)
{
classID = new Guid("3A9B8F47-60A0-43F0-AFE2-73D08511668D");
}
public void InitNew()
{
}
public void Load(IPropertyBag propertyBag, int errorLog)
{
object val = null;
try
{
propertyBag.Read("NewNamespace", out val, 0);
}
catch(ArgumentException)
{
}
catch(Exception ex)
{
throw new ApplicationException("Error reading PropertyBag: " + ex.Message);
}
if (val != null)
_newNamespace = (string)val;
else
_newNamespace = "http://AnonymousURL";
}
public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
{
object val = (object)_newNamespace;
propertyBag.Write("NewNamespace", ref val);
}
#endregion
`NewNamespace` 属性用于在设计时配置新命名空间。
`Load` 方法将 `PropertyBag` 中的值读取到 `NewNamespace` 属性中。在管道设计器中将组件添加到解码阶段时,我遇到了一个错误消息。然而,该组件工作正常,但为了避免此错误消息,我添加了一个 `catch` 块来捕获 `ArgumentException`。
`Save` 方法将 `NewNamespace` 属性中的值保存到 `PropertyBag` 中。
`GetClassID` 返回组件的唯一标识值。
InitNew
用于初始化要持久化的对象到组件属性中。此项目不需要。
核心接口是 `IComponent`。在此项目中,它包含一个方法,用于执行管道组件来处理输入消息并获取结果消息。
#region IComponent
public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
IBaseMessagePart bodyPart = pInMsg.BodyPart;
if(bodyPart != null)
{
Stream originalStream = bodyPart.GetOriginalDataStream();
if(originalStream!=null)
{
using (XmlReader reader = XmlReader.Create(originalStream))
{
XmlWriterSettings ws = new XmlWriterSettings();
ws.Indent = true;
ws.ConformanceLevel = ConformanceLevel.Auto;
MemoryStream outputStream = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(outputStream, ws))
{
reader.Read();
if (reader.NodeType == XmlNodeType.XmlDeclaration)
{
writer.WriteStartDocument();
reader.Read();
}
// Root Node
if (reader.NodeType == XmlNodeType.Element)
writer.WriteStartElement(reader.Name, _newNamespace);
reader.Read();
while (reader.NodeType == XmlNodeType.Element)
{
writer.WriteNode(reader, true);
}
}
outputStream.Position = 0;
bodyPart.Data = outputStream;
}
}
}
return pInMsg;
}
#endregion
}// Ends the class
}// Ends the namespace
在上面的代码中,我首先检查输入流是否包含数据,然后我创建一个 `XmlReader` 从原始流读取,创建一个 `XmlWriter` 写入输出流。
创建编写器后,`reader.Read()` 行读取 XML 消息中的第一个节点。如果第一个节点是 XML 声明,那么编写器会将 XML 声明写入输出流,然后读取器读取下一个节点。
此时读取器指向根节点,这里是更改命名空间的位置。编写器通过将新元素写入输出流来实现这一点。这将是输出消息中的根节点。 `writer.WriteStartElement(reader.Name, _newNamespace)` 行执行此操作。第一个参数是元素的名称(根节点),第二个参数是新命名空间。请记住,`_newNamespace` 是在 `IPersistPropertyBag` 部分定义的。
接下来,`reader.read()` 行读取根节点内的第一个节点。在 `while` 循环内部,编写器将根节点内的所有节点及其所有内容写入输出流。输出流现在已完成,并包含与输入流相同的数据。
为了完成代码,我将输出流的位置设置为流的开头,并将输出流分配给消息的 `bodyPart` 的 `Data` 属性。然后我返回消息。
安装和测试组件
要使用此组件,需要构建项目,然后可以将 DLL 复制到 BTS 安装位置的“Microsoft BizTalk Server 2006\Pipeline Components”文件夹中。(不过,也可以将其复制到任何位置。)
在 BTS 接收管道项目中,必须将此组件 DLL 添加到工具栏并在解码阶段使用。 `NewNamespace` 属性的值可以在管道设计时设置。部署管道后,也可以在 BTS 管理控制台的接收位置中配置管道时设置此属性。
提示:如果在管道中使用组件后需要更改和重新构建该组件,则在构建组件时可能会遇到问题,因为 DLL 文件可能被另一个进程使用。要解决此问题,我使用 `WhoLockedMe` 应用程序来查看哪个进程锁定了文件,然后可以终止这些进程。 `WhoLockedMe` 是免费的,可以在网上轻松搜索并下载。
历史
- 2011 年 1 月 14 日:初次发布