Создание сообщения вручную внутри оркестровки

Вот ситуация. У меня есть последовательность из 12 задач интеграции, которые нужно запускать каждые 15 минут, большинство из них на самом деле считывают что-то с сервера оракула и отправляют это в веб-службу. Я создал порт как для оракула, так и для веб-службы, а также создал основную оркестровку, которая зацикливается каждые 15 минут и вызывает другие оркестровки, которые будут выполнять свои задачи.

Теперь моя проблема заключается в том, что эти оркестровки не вызываются прибытием сообщения, и мне нужно создать сообщение, которое я отправлю на порт оракула. Тот, который будет выглядеть так:

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

Я знаю, какими будут значения узла, но я не знаю, как построить сообщение, кроме как использовать «магические строки» и конкатенировать строки, которые я буду загружать в xmlDoc с помощью LoadXml, а затем назначать это параметрам сообщения, которые мне бы очень хотелось избегать по многим причинам (начиная с изменения пространства имен в будущем). Есть ли способ для оркестровки создать «пустое» сообщение, которое я затем заполню?

Может быть, вопрос очень простой, и я не вижу дерева из леса, но все образцы, которые я видел в сети, упрощены (то есть кто-то просто бросает готовый xml в отслеживаемую папку, чтобы вызвать оркестрацию) и не помогают мне .


person mmix    schedule 01.02.2012    source источник


Ответы (4)


Вот решение, которое я реализовал для аналогичной проблемы: как предлагает Хью, я использую помощник, наследуемый от 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);
            }
        }
    }
}

Конечно, мой пример предназначен для установки фиксированного атрибута value, поэтому вам придется адаптировать его к вашим потребностям.

Вспомогательный класс 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 в свой проект и измените действие компиляции на Embedded Resource, чтобы ResorceXmlDocument мог загрузить его через 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>

Переменные оркестровки и сообщения

Вам нужно объявить

  • переменная *queryValues* типа `Acme.QueryValues`
  • переменная *resourceXmlDoc* типа `Acme.ResourceXmlDocument`
  • сообщение типа `MySchemaType`

Объединение в форму назначения сообщений

внутри формы Construct Message, создающей Message MyRequest типа 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;

Я храню ResourceXmlDocument и QueryValues в служебной библиотеке и ссылаюсь на нее из любого проекта BizTalk, который мне нужен. Различные документы шаблона Xml встроены в соответствующую сборку BizTalk.

EDIT by OP: На самом деле единственный способ заставить это работать - также реализовать ISerializable на ResourceXmlDocument и сохранить сообщение, используя пользовательскую сериализацию 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);
    }
person Filburt    schedule 01.02.2012
comment
@nonnb Всегда рад помочь. В нашей среде это оказалось довольно надежным решением. - person Filburt; 01.02.2012
comment
Я попробую это, но я только что узнал кое-что. Когда я показал все файлы после использования адаптера, я обнаружил файл .cs, который не был частью проекта. Файл содержал сериализуемые классы для всех элементов схемы (автогенерация Microsoft.BizTalk.Schema.Compiler, 3.0.1.0). Но если я включаю файл, он все равно не видит его в заданной форме. - person mmix; 01.02.2012
comment
@mmix Вам необходимо объявить переменную этого типа (представление оркестровки), чтобы иметь возможность использовать ее в своей форме назначения. - person Filburt; 02.02.2012
comment
Нет, объявление var не работает по той же причине, по которой не работает предложение Хью. Кажется, что ваше решение - единственное разумное, что является очень странным ограничением Biztalk, имхо. Неужели они настолько оторваны от реальности, что не думают, что кто-то захочет создать сообщение с нуля? В любом случае, ради последовательности я попробую ваше предложение и приму этот ответ, если он сработает. - person mmix; 02.02.2012
comment
@mmix Ну, в основном каждая форма сообщения Construct создает сообщение с нуля - сложная часть в вашем случае состоит в том, чтобы заполнить значения (вместо сопоставления или копирования исходного сообщения). Если вы продвигаете каждое поле (что будет работать только в том случае, если они не являются повторяющимися узлами), вы можете просто присвоить значения. - person Filburt; 02.02.2012
comment
Да, но теоретически .NET достаточно мощен, чтобы представить любой мыслимый xsd в виде класса/набора классов и, таким образом, разрешить строго типизированное создание и заполнение экземпляров, которые будут сериализованы в сообщения, соответствующие xsd. Однако в проекте BT такой простой подход невозможен. Без разницы. Зачем вообще использовать .NET в BT, если вы просто выбрасываете наиболее полезную часть? - person mmix; 02.02.2012
comment
Хорошо, я как бы разработал решение, похожее на ваше, поэтому я дам вам ответ. - person mmix; 13.02.2012
comment
Хм, новые проблемы. Теперь, когда сообщение создано, я получаю Type 'System.Xml.XmlDocument' in Assembly 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable. Я не понимаю его, resourceXmlDoc помечен как сериализуемый... - person mmix; 13.02.2012

Создайте статическую вспомогательную функцию, которая возвращает тип XmlDocument. Вызовите эту функцию из формы назначения.

Внутри вспомогательной функции вы можете загрузить настройки (пространство имен и т. д. или даже полное сообщение) из файла конфигурации или текстового файла.

Для лучшей практики вы должны хранить эту конфигурацию в SSO. Если вам нужна помощь с этим, дайте мне знать.

person tom redfern    schedule 01.02.2012
comment
Разве для этого не потребуется внешняя библиотека? Если да, то как синхронизировать схемы xsd между проектами? У вас есть какой-нибудь учебник, которому я могу следовать? - person mmix; 01.02.2012
comment
Вы можете создать статический помощник в той же сборке, что и оркестровка, если вам не нужна внешняя сборка. - person tom redfern; 01.02.2012
comment
Я собирался спросить, как, но затем я скопировал / вставил файл cs... кажется, все в порядке. Это поможет мне и в других вещах, поэтому я пока проголосую за это. - person mmix; 01.02.2012
comment
на самом деле никакой радости, класс есть в проекте bt, он на это не жалуется, но в назначенной форме я не вижу типа. - person mmix; 01.02.2012
comment
Вы уверены, что класс находится в том же пространстве имен, что и ваш тип оркестровки? - person tom redfern; 01.02.2012
comment
Да, то же самое пространство имен (я даже проверил с помощью Reflector, класс _orchestration [extends BTXService] находится в том же пространстве имен. Тем не менее, он не отображается в диалоговом окне назначения, и попытка ввести его все равно приводит к ошибке. Как добавить класс в BT проекта, может я что не так делаю. - person mmix; 01.02.2012
comment
Хорошо, если это статический класс, вам не нужно добавлять его в оркестровку. Однако, если вам нужен его экземпляр в вашем orch, вы должны создать переменную orch типа your-class, а затем создать ее экземпляр в форме выражения. - person tom redfern; 01.02.2012
comment
Нет, статический класс не виден, а класс экземпляра не указан в списке «Выбор типа артефакта», когда я пытаюсь создать переменную. может быть, я делаю что-то не так, будет ли вам слишком сложно просто создать скелет проекта BT 2010 с одним пользовательским классом, одной оркестровкой и двумя связанными? - person mmix; 02.02.2012
comment
ОК, невозможно вызвать класс С# из того же проекта, что и оркестровка. См. здесь social.msdn.microsoft. com/Forums/ar/biztalkgeneral/thread/ - person tom redfern; 02.02.2012

Йосси Дахан сравнивает эти методы (сопоставление, назначение и использование недокументированного API) здесь

Метод API использует Microsoft.BizTalk.Component.Interop.DocumentSpec — ссылки здесь и здесь, но, как упоминает Йосси, намного медленнее чем карты или XmlDocument.LoadXml

Несколько замечаний по использованию:

  • Сборка — это TestSchema, версия = 1.0.0.0, культура = нейтральная, publicKeyToken = xxxxxxxxxxxxxxxx»;
  • имя_схемы — TestSchema.MyTestSchema[+myRootNode1]
  • Примечание зависит от версии — если версия сборки изменится, создание не удастся, если не обновить строку версии.
  • Новое сообщение, созданное таким образом, не обязательно допустимо для XSD. например DateTimes и Ints будут просто пустыми элементами, даже если они могут быть нулевыми (и это не установит nillable=true в XML)
person StuartLC    schedule 01.02.2012
comment
Йосси говорит, но не ходит, я думаю, он предполагает, что мы все находимся на этом уровне. Может быть, когда-нибудь, но не сегодня, поскольку он не делал никаких образцов, я действительно понятия не имею, как использовать его предложения. Во-вторых, я не хочу использовать недокументированные функции (они всегда приводят к неприятным телефонным звонкам спустя годы). - person mmix; 01.02.2012

Рассматривали ли вы возможность использования формы преобразования для создания экземпляра схемы, которую вы хотите отправить в ORACLE?

Это одна из альтернатив созданию сообщения в форме назначения сообщения. Дайте мне знать, если вам нужна более подробная информация!

ХТН

person TJ Amas    schedule 01.02.2012
comment
Для преобразования требуется исходное сообщение, а у меня его нет. - person mmix; 01.02.2012