1

使用 BizTalk 2013r2 CU1,我为我的入站 xsd 创建了一个属性架构并部署了应用程序。

当我使用标准的“xml 接收”管道接收示例 xml 文档时,我可以看到所需的元素已按预期提升到上下文中。

然后,我创建了一个自定义管道,该管道在“Disassemble”阶段包含“XML disassembler”组件,在“Validate”阶段包含一个自定义组件。这个自定义组件需要从上下文中读取提升的属性。但是,我发现当我将接收位置从“xml 接收”管道切换到我的自定义管道时,我的属性不会得到提升。我在我的自定义组件中使用以下代码来写出消息上下文中的项目列表:

for (int x = 0; x < contextList.CountProperties; x++)
        {
            contextList.ReadAt(x, out name, out nspace);
            string value = contextList.Read(name, nspace).ToString();
            contextItems += "Name: " + name + " - " + "Namespace: " + nspace + " - " + value + "\r\n";
            if (name == _ContextPropertyName && nspace == _ContextPropertyNamespace)
                promotedPropFound = true;

        }
        Helpers.EventLogHelper eventHelper = new EventLogHelper();
        eventHelper.LogEvent(string.Format("Context items:{0}", contextItems));

        if (promotedPropFound == false)
            throw new Exception(string.Format("Unable to find promoted property with name[{0}] and namespace [{1}]", _ContextPropertyName, _ContextPropertyNamespace));

从事件日志的输出中,我可以看到某些属性(例如 MessageType)已被提升,但我的自定义属性没有。同样,如果我将接收位置改回使用标准的“xml 接收”管道,则该属性将从同一个 xml 文档的副本中提升(我通过停止订阅发送端口并从管理控制台查看上下文来检查这一点)。

我觉得这很奇怪,因为相同的“XML 反汇编器”组件存在于两个管道的相同“反汇编”阶段,具有相同的(默认)配置。我开始认为 2013r2CU1 可能存在问题 - 还有其他人遇到过同样的问题吗?

4

2 回答 2

7

当 XML Disassembler 在您的自定义管道中执行时,不能保证您的属性已被提升。

传入消息以流的形式到达管道,数据指针设置在流的开头。
我认为 XML Disassembler 不会读取流,它会将其包装到某个流包装类中,该类将在实际读取流时填充提升的属性。
该流必须至少被读取一次:当消息被插入消息框时。所以可以保证属性会得到提升,但你不能假设它会在“验证”阶段执行之前完成。

要确保这确实是您遇到的问题:在将消息导入消息框后检查您的消息。
如果您的促销财产在那里,我所描述的可能就是正在发生的事情。

解决方案:

为了使您的自定义管道组件正常工作,最好的解决方案是像 XML Disassembler 一样:获取传入流并将其包装到一个流包装器类中,该类可以触发您需要的任何功能。

Microsoft.BizTalk.Streaming.dll 程序集有一些您可能感兴趣的包装类:ForwardOnlyEventingReadStream。
此类有一个事件 AfterLastReadEvent。您可以创建一些 EventHandler 并让它订阅此事件以仅在完全读取流之后触发您的自定义功能。并且所有属性都已提升。

您的自定义组件将如下所示:

public IBaseMessage Execute(IPipelineContext context, IBaseMessage message)
{
    Stream stream = message.BodyPart.GetOriginalDataStream();
    CForwardOnlyEventingReadStream eventingReadStream = new CForwardOnlyEventingReadStream(stream);
    eventingReadStream.AfterLastReadEvent += new AfterLastReadEventHandler(DoSomething);

    message.BodyPart.Data = eventingReadStream; 
    return message;
}

private static void DoSomething(object src, EventArgs args)
{
}

解决问题的一种效率较低的方法是在“验证”阶段在自定义组件中完全读取流,并将流指针放回流的开头。

当您在管道组件中操作消息流时,Microsoft 有一些指导方针: https ://msdn.microsoft.com/en-us/library/aa577699.aspx

更新:

OP 需要将消息上下文传递给事件处理程序。可以使用 Lambda 表达式:

public IBaseMessage Execute(IPipelineContext context, IBaseMessage message)
{
    Stream stream = message.BodyPart.GetOriginalDataStream();
    CForwardOnlyEventingReadStream eventingReadStream = new CForwardOnlyEventingReadStream(stream);
    eventingReadStream.AfterLastReadEvent += new AfterLastReadEventHandler((src, args) => DoSomething(src, args, message.Context));

    message.BodyPart.Data = eventingReadStream; 
    return message;
}

private static void DoSomething(object src, EventArgs args, IBaseMessageContext messageContext)
{
}

这个 SO 问题对于传递附加参数可能很有趣:将参数 传递给 EventHandler

于 2016-01-03T07:45:49.587 回答
2

你能做你为编排中的验证阶段计划的任何事情吗?那会容易得多。

如果不是,这个特定问题的最常见解决方案是一个中间管道组件,它强制对流进行完整读取,尽管从技术上讲,您只需要读取,直到命中提升节点。

于 2016-01-05T14:34:35.260 回答