在编排内手动构建消息

发布于 2025-01-01 12:47:30 字数 670 浏览 6 评论 0 原文

情况如下。 我每 15 分钟运行一次 12 个集成任务,其中大多数实际上是从 Oracle 服务器读取某些内容并将其推送到 Web 服务中。我已经为 Oracle 和 Web 服务创建了一个端口,并且创建了一个主编排,该编排每 15 分钟循环一次,并调用其他编排来执行其任务。

现在,我的问题是这些编排不是由消息到达调用的,并且我需要构造一条将发送到 oracle 端口的消息。看起来像这样的:

<Select xmlns="http://Microsoft.LobServices.OracleDB/2007/03/HR/Table/EMPLOYEES">
    <COLUMN_NAMES>*</COLUMN_NAMES>
    <FILTER>DATE=somedate</FILTER>
</Select>

我知道节点值是什么,但我不知道如何构造消息,除了使用“魔术字符串”和连接字符串,我将使用 LoadXml 加载到 xmlDoc 中,然后将其分配给由于很多原因,我非常希望避免使用消息参数(从将来命名空间的更改开始)。有没有一种方法可以让编排创建“空白”消息,然后我将填充该消息?

也许问题很简单,我看不到森林里的树,但我在网上看到的所有示例都是简化的(意味着有人只是在监视文件夹中放置一个现成的 xml 来调用编排)并且对我没有帮助。

Here' the situation.
I have a sequence of 12 integration tasks to be run every 15 minutes, most of them actually reading something from the oracle server and pushing it into a web service. I have created a port for both oracle and web service and I created a main orchestration which loops every 15 minutes and calls other orchestrations that will do their tasks.

Now, my problem is that those orchestrations are not invoked by a message arrival, and I have a need to construct a message that I will send to the oracle port. The one that will look like this:

<Select xmlns="http://Microsoft.LobServices.OracleDB/2007/03/HR/Table/EMPLOYEES">
    <COLUMN_NAMES>*</COLUMN_NAMES>
    <FILTER>DATE=somedate</FILTER>
</Select>

I know what the node values will be but I do not know how to construct the message other than to use "magic strings" and concatenating strings that I will load into xmlDoc using LoadXml and then assigning that to message parameters which I would very much like to avoid for a lot of reasons (starting with a change in namespace in the future). Is there a way for orchestration to create the "blank" message which I will then fill in?

Maybe the question is very simple and I can't see the tree from the forest, but all the samples I saw on the net are simplified (meaning someone just drops a ready xml in a watched folder to invoke orchestration) and do not help me.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

我也只是我 2025-01-08 12:47:30

这是我针对类似问题实现的解决方案:正如 Hugh 建议的那样,我使用从 XmlDocument 继承的助手。

Xml 模板类

using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml;

namespace Acme
{
    [Serializable]
    public class ResourceXmlDocument : XmlDocument
    {
        public ResourceXmlDocument(Type assemblyType, string resourceName, QueryValues queryValues)
        {
            try
            {
                Assembly callingAssembly = Assembly.GetAssembly(assemblyType);

                if (null == callingAssembly)
                {
                    throw new ResourceException("GetExecutingAssembly returned null");
                }

                Stream resourceStream = callingAssembly.GetManifestResourceStream(resourceName);

                Load(resourceStream);

                if (null == queryValues)
                {
                    throw new ResourceException("queryValues not initialized");
                }

                if (queryValues.Keys.Count < 1)
                {
                    throw new ResourceException("queryValues.Keys must have at least one value");
                }


                foreach (string querycondition in queryValues.Keys)
                {
                    XmlNode conditionNode = this.SelectSingleNode(querycondition);

                    if (null == conditionNode)
                    {
                        throw new ResourceException(string.Format(CultureInfo.InvariantCulture, "Condition: '{0}' did not return a XmlNode", querycondition));
                    }

                    XmlAttribute valueAttribute = conditionNode.Attributes["value"];

                    if (null == valueAttribute)
                    {
                        throw new ResourceException(string.Format(CultureInfo.InvariantCulture, "Condition: '{0}' with attribute 'value' did not return an XmlAttribute ", querycondition));
                    }

                    valueAttribute.Value = queryValues[querycondition];
                }
            }
            catch (Exception ex)
            {
                throw new ResourceException(ex.Message);
            }
        }
    }
}

当然,我的示例以要设置的固定属性为目标,因此您必须根据您的需要进行调整。

QueryValues 帮助器类

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace Acme
{
    [Serializable]
    public class QueryValues : Dictionary<string, string>
    {
        public QueryValues()
        {
        }


        protected QueryValues(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }
}

Xml 模板

将 Xml 文档 MyTemplate.xml 添加到您的项目中,并将编译操作更改为嵌入资源< /code> 因此 ResorceXmlDocument 可以通过反射加载它。

<?xml version="1.0" encoding="utf-8" ?>
<root>
    <SomeOtherNode>some (fixed) value</SomeOtherNode>
    <MyNodeName tablename="MyTableName" fieldname="MyFieldName" value="0" />
    <YetAnotherNode>
        <SubNode>Foo</SubNode>
    </YetAnotherNode>
</root>

编排变量和消息

您需要声明

  • “Acme.QueryValues”类型的变量 *queryValues*
  • 、“Acme.ResourceXmlDocument”类型的变量 *resourceXmlDoc*、
  • “MySchemaType”类型的消息

将其放在消息分配形状内

在构造消息形状内创建 MySchemaType 类型的消息 MyRequest

queryValues = new Acme.QueryValues();

queryValues.Add("//MyNodeName[@tablename='MyTableName' and @fieldname='MyFieldName']", "MyValueToSet");

resourceXmlDoc = new Acme.ResourceXmlDocument(typeof(Acme.MySchemaType), "MyTemplate.xml", queryValues);

MyRequest = resourceXmlDoc;

我保留util lib 中的 ResourceXmlDocumentQueryValues 并从我需要的任何 BizTalk 项目中引用它。各种 Xml 模板文档嵌入到相应的 BizTalk 程序集中。

由OP编辑:实际上,我进行此工作的唯一方法是在ResourceXmlDocument上实现ISerialized,并使用OuterXml的自定义序列化保留消息。基础中的 XmlDocument 本身无法序列化。如果有其他方法,请随意编辑。

[Serializable]
public class ResourceXmlDocument : XmlDocument, ISerializable
{

    ...

    protected ResourceXmlDocument(SerializationInfo info, StreamingContext context)
    {
        if (info == null) throw new System.ArgumentNullException("info");
        Load(info.GetString("content"));
    }


    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (info == null) throw new System.ArgumentNullException("info");
        info.AddValue("content", this.OuterXml);
    }

Here's a solution I implemented for a similar problem: As Hugh suggests I use a helper inheriting from XmlDocument.

The Xml Template class

using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml;

namespace Acme
{
    [Serializable]
    public class ResourceXmlDocument : XmlDocument
    {
        public ResourceXmlDocument(Type assemblyType, string resourceName, QueryValues queryValues)
        {
            try
            {
                Assembly callingAssembly = Assembly.GetAssembly(assemblyType);

                if (null == callingAssembly)
                {
                    throw new ResourceException("GetExecutingAssembly returned null");
                }

                Stream resourceStream = callingAssembly.GetManifestResourceStream(resourceName);

                Load(resourceStream);

                if (null == queryValues)
                {
                    throw new ResourceException("queryValues not initialized");
                }

                if (queryValues.Keys.Count < 1)
                {
                    throw new ResourceException("queryValues.Keys must have at least one value");
                }


                foreach (string querycondition in queryValues.Keys)
                {
                    XmlNode conditionNode = this.SelectSingleNode(querycondition);

                    if (null == conditionNode)
                    {
                        throw new ResourceException(string.Format(CultureInfo.InvariantCulture, "Condition: '{0}' did not return a XmlNode", querycondition));
                    }

                    XmlAttribute valueAttribute = conditionNode.Attributes["value"];

                    if (null == valueAttribute)
                    {
                        throw new ResourceException(string.Format(CultureInfo.InvariantCulture, "Condition: '{0}' with attribute 'value' did not return an XmlAttribute ", querycondition));
                    }

                    valueAttribute.Value = queryValues[querycondition];
                }
            }
            catch (Exception ex)
            {
                throw new ResourceException(ex.Message);
            }
        }
    }
}

Of course my expample targets a fixed attribute value to be set so you'll have to adapt this to your needs.

The QueryValues helper class

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace Acme
{
    [Serializable]
    public class QueryValues : Dictionary<string, string>
    {
        public QueryValues()
        {
        }


        protected QueryValues(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }
}

The Xml Template

Add a Xml doc MyTemplate.xml to your project and change the compile action to Embedded Resource so ResorceXmlDocument can load it via Reflection.

<?xml version="1.0" encoding="utf-8" ?>
<root>
    <SomeOtherNode>some (fixed) value</SomeOtherNode>
    <MyNodeName tablename="MyTableName" fieldname="MyFieldName" value="0" />
    <YetAnotherNode>
        <SubNode>Foo</SubNode>
    </YetAnotherNode>
</root>

Orchestration variables and Messages

You'll need to declare

  • a variable *queryValues* of type `Acme.QueryValues`
  • a variable *resourceXmlDoc* of type `Acme.ResourceXmlDocument`
  • a message of type `MySchemaType`

Putting it together inside a Message Assignment Shape

inside a Construct Message Shape creating a Message MyRequest of type MySchemaType

queryValues = new Acme.QueryValues();

queryValues.Add("//MyNodeName[@tablename='MyTableName' and @fieldname='MyFieldName']", "MyValueToSet");

resourceXmlDoc = new Acme.ResourceXmlDocument(typeof(Acme.MySchemaType), "MyTemplate.xml", queryValues);

MyRequest = resourceXmlDoc;

I'm keeping ResourceXmlDocument and QueryValues in a util lib and reference it from any BizTalk project I need. The various Xml template docs are embedded into the respective BizTalk assembly.

EDIT by OP: Actually the only way I go this to work is to also implement ISerializable on ResourceXmlDocument and persist message using custom serialization of OuterXml. The XmlDocument in the base is simply not serializable on its own. If there is another approach, feel free to edit this.

[Serializable]
public class ResourceXmlDocument : XmlDocument, ISerializable
{

    ...

    protected ResourceXmlDocument(SerializationInfo info, StreamingContext context)
    {
        if (info == null) throw new System.ArgumentNullException("info");
        Load(info.GetString("content"));
    }


    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (info == null) throw new System.ArgumentNullException("info");
        info.AddValue("content", this.OuterXml);
    }
眼前雾蒙蒙 2025-01-08 12:47:30

创建一个返回 XmlDocument 类型的静态辅助函数。从您的指定形状中调用此函数。

在辅助函数内,您可以从配置或文本文件加载设置(命名空间等,甚至完整消息)。

为了获得最佳实践,您应该将此配置存储在 SSO 中。如果您需要这方面的帮助,请告诉我。

Create a static helper function that returns a type of XmlDocument. Call this function from within your assign shape.

Inside the helper function you could load settings (namespace etc, or even the full message) from a config or text file.

For best practise you should store this config in SSO. If you need help with this let me know.

回忆凄美了谁 2025-01-08 12:47:30

Yossi Dahan 比较了这些方法(映射、分配和使用未记录的 API)此处

API 方法使用 Microsoft.BizTalk.Component.Interop.DocumentSpec - 引用 此处此处,但作为 Yossi提到,比地图或 XmlDocument.LoadXml 慢得多,

只是关于用法的一些注释:

  • Assembly is TestSchema, Version=1.0.0.0, Culture=neutral, publicKeyToken=xxxxxxxxxxxxxxxx”;
  • schemaName 是 TestSchema.MyTestSchema[+myRootNode1]
  • 注意版本相关 - 如果程序集版本发生更改,则创建将失败,除非也更新版本字符串。
  • 以这种方式创建的新消息不一定对 XSD 有效。例如,DateTimes 和 Ints 将只是空元素,即使它们可以为 nillable(并且这不会在 XML 中设置 nillable=true)

Yossi Dahan compares these methods (map, assign, and using an undocumented API) here

The API method uses Microsoft.BizTalk.Component.Interop.DocumentSpec - references here and here, but as Yossi mentions, is much slower than maps or XmlDocument.LoadXml

Just some notes on the usage:

  • Assembly is TestSchema, Version=1.0.0.0, Culture=neutral, publicKeyToken=xxxxxxxxxxxxxxxx”;
  • schemaName is TestSchema.MyTestSchema[+myRootNode1]
  • Note is Version dependent - is that if the Assembly Version changes, then creation will fail unless also update version string.
  • The new Message Created in this way isn't necessarily valid against the XSD. e.g. DateTimes and Ints will just be empty elements, even if they are nillable (and this won't set the nillable=true in the XML)
别在捏我脸啦 2025-01-08 12:47:30

您是否考虑过使用转换形状来创建要发送到 ORACLE 的模式实例?

这是在消息分配形状中创建消息的一种替代方法。
如果您需要更多详细信息,请告诉我!

华泰

Have you considered using a transform shape to create an instance of the schema you want to send on to ORACLE?

That's one alternative to creating the message in the Message Assignment shape.
Let me know if you require more detail!

HTH

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文