2

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.

mmix
  • 6,057
  • 3
  • 39
  • 65

4 Answers4

2

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.

tom redfern
  • 30,562
  • 14
  • 91
  • 126
  • Won't this require external library? If so how to keep xsd schemas synced between projects? Do you have some tutorial I can follow on this? – mmix Feb 01 '12 at 15:02
  • 1
    You could create the static helper in the same assembly as the orchestration if you didn't want an external assembly. – tom redfern Feb 01 '12 at 15:39
  • I was about to ask how, but then I copy/pasted a cs file over... it seems to build ok. This will help me on other stuff as well, so I'll upvote it for now. – mmix Feb 01 '12 at 15:50
  • actually, no joy, the class is in bt project, it doesn't complain about it but in the assign shape I do not see the type. – mmix Feb 01 '12 at 16:01
  • Are you sure the class is in the same namespace as your orchestration type? – tom redfern Feb 01 '12 at 16:22
  • Yup, the same namespace (I even checked with reflector, the _orchestration [extends BTXService] class is in the same namespace. Yet its not viible in assign dialog and trying to enter it anyway results in error. How do you add a class to BT project, maybe I am not doing this right. – mmix Feb 01 '12 at 17:29
  • Well if it's a static class you don't need to "add" it to the orchestration. However if you need an instance of it in your orch then you should create an orch variable of type *your-class* and then instantiate it in an expression shape. – tom redfern Feb 01 '12 at 19:34
  • Nope, the static class is not visible and instance class is not listed in "Select Artifact Type" when I try to create a variable. maybe I am doing something wrong, would it be too difficult for you to just create a skeleton BT 2010 project with one custom class and one orchestration and the two linked? – mmix Feb 02 '12 at 10:54
  • OK it's not possible to call a c# class from the same project as the orchestration. See here http://social.msdn.microsoft.com/Forums/ar/biztalkgeneral/thread/6d1faebe-63be-4578-b67e-aac06e2e6c57 – tom redfern Feb 02 '12 at 11:30
2

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);
    }
mmix
  • 6,057
  • 3
  • 39
  • 65
Filburt
  • 17,626
  • 12
  • 64
  • 115
  • @nonnb Always glad to help. It turned out to be a quite robust solution in our environment. – Filburt Feb 01 '12 at 15:30
  • I'll try this, but I just found out something. When I showed all files ater consuming adapter I found a .cs file that was not part of the project. The file contained serializable classes for all schema elements (auto gen by Microsoft.BizTalk.Schema.Compiler, 3.0.1.0). But if I include the file it still does not see it in assign shape. – mmix Feb 01 '12 at 16:04
  • @mmix You need to declare a variable of this type (Orchestration View) to be able to use it in your Assign Shape. – Filburt Feb 01 '12 at 22:23
  • No, declaring a var doesn't work, for the same reason Hugh's suggestion does not work. It seems that your solution is the only reasonable one which is very bizzare limitation of Biztalk, imho. Are they really that detached from reality that they don't think anyone would want to create a message from scratch? Anyway, jut for the sake of consistency I'll try your suggestion and accept thsi answer if it works. – mmix Feb 02 '12 at 12:30
  • @mmix Well basically every Construct Message Shape creates a message from scratch - the tricky part in your case is to fill in the values (instead of mapping or copying a source message). If you promote every field (which will only work if they are non-repeating nodes) you could simply assign values. – Filburt Feb 02 '12 at 13:38
  • Yes, but in theory .NET is powerful enough to represent every conceivable xsd as a class/set of classes and thus allow strong-typed creation and population of instances that will be serialized into messages conforming to xsd. However in BT project this simple approach is impossible. Makes no sense. Why use .NET in BT at all if you just throw away the most usable part? – mmix Feb 02 '12 at 19:54
  • Ok, I kind of designed a similar solution to yours, so I'll award you the answer. – mmix Feb 13 '12 at 16:25
  • Hmm, new problems. Now that the message is constructed I get `Type 'System.Xml.XmlDocument' in Assembly 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable.` I don't understand it, the resourceXmlDoc is marked as serializable... – mmix Feb 13 '12 at 18:41
1

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)
StuartLC
  • 104,537
  • 17
  • 209
  • 285
  • Yossi talks the talk, but doesn't walk the walk, I guess he assumes we are all at this level. Maybe one day, but not today, sicne he made no samples whatsoever I really have no idea how to use his suggestions. As for second I do not want to use undocumented features (they always lead to nasty phone calls years later) – mmix Feb 01 '12 at 15:57
0

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

TJ Amas
  • 1,291
  • 13
  • 19