解决您的问题的规范方法确实是在传入管道的解码阶段使用自定义管道组件。
这里有两种方法可以有效地工作。
使用 BizTalk ESB Tookit 管道组件
BizTalk ESB Toolkit 2.1 有一组非常强大的管道组件来操作传入消息中的 XML 命名空间。此管道在 Microsoft.Practices.ESB.Namespace.PipelineComponents.dll 程序集中可用,安装后可用于 Visual Studio 工具箱。
这些组件的早期版本记录在 MSDN 上。尽管此文档已经过时,但我认为新版本没有太大变化。尽管如此,我还是建议您参考各种论坛问题以正确使用和参考。
构建一个简单的 AddXmlNamespace 管道组件
如果您不想或无法在您的环境中安装 BizTalk ESB Toolkit,我建议您自己构建该组件的简化版本。这一点都不难,这要归功于您可以利用的 BizTalk 运行时中的内置类。
我在下面显示了所需的代码,因为您在问题中显示的代码不符合正确的支持流式传输的管道组件。请注意,下面显示的代码仅处理在原始根标记上添加 XML 命名空间(如果尚不存在)。
首先,您需要构建一个简单的 System.IO.Stream 派生类,用于处理在原始文档的根标记上添加 XML 命名空间和前缀。
为了支持流式传输,代码利用了Microsoft.BizTalk.Streaming.XmlTranslatorStream类。此类展示了一个类似 SAX 的接口,从而在 XML 解析阶段的各个点调用实现的覆盖。所有这些都是在保持对流式传输任意大文档的完全支持的同时执行的。
这是代码:
using Microsoft.BizTalk.Streaming;
public class AddXmlNamespaceStream : XmlTranslatorStream
{
private String namespace_;
private int level_ = 0; // hierarchy level
public AddXmlNamespaceStream(Stream stream, String @namespace)
: base(XmlReader.Create(stream))
{
namespace_ = @namespace;
}
#region XmlTranslatorStream Overrides
protected override void TranslateStartElement(string prefix, string localName, string nsURI)
{
if (level_++ != 0)
{
base.TranslateStartElement(prefix, localName, nsURI);
return;
}
if (String.IsNullOrEmpty(nsURI))
{
nsURI = namespace_;
if (String.IsNullOrEmpty(prefix))
prefix = "__bts_ns0__";
}
base.TranslateStartElement(prefix, localName, nsURI);
}
protected override void TranslateEndElement(bool full)
{
if (level_-- != 0)
{
base.TranslateEndElement(full);
return;
}
base.TranslateEndElement(full);
}
#endregion
}
您会注意到这个类覆盖了TranslateStartElement和TranslateEndElement方法,但只处理层次结构第一级的元素 - 即根标记。所有其他元素都根据基类提供的默认不做特殊行为进行处理。
至于管道组件本身,我将在这里只展示它的 Execute 方法,因为您可能已经设置了所有必需的样板基础设施代码。如果不是这样,请参阅以下位置的博客文章,从底部开始。
这里是:
public class AddXmlNamespace : ..., IComponent
{
#region Design-Time Properties
public String Namespace { get; set; }
#endregion
#region IComponent Implementation
public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
var stream = new AddXmlNamespaceStream(
pInMsg.BodyPart.GetOriginalDataStream()
, Namespace);
pInMsg.BodyPart.Data = stream;
pContext.ResourceTracker.AddResource(stream);
return pInMsg;
}
#endregion
...
}
如您所见,Execute 方法执行的唯一操作是将原始流包装在新的 AddXmlNamespace 流类的实例中,并将其连接起来,以便替换传入的消息流。
希望这可以帮助。