Question

I am attempting to develop a generic BizTalk application for configuring dynamic ports. I have an orchestration that pulls back all the configuration settings for each port and I want to loop through these settings and configure the ports. The settings are held in MSSQL and, for instance, two of the properties are PortName and Address. So from within the orchestration I would like to reference the port by the string variable PortName. So is there some way to get a collection of all the ports in an orchestration or reference a port via a string variable i.e. Port['MyPortName'](Microsoft.XLANGs.BaseTypes.Address) = "file://c:\test\out\%MessageId%.xml" Thanks

Was it helpful?

Solution

In the first place you shouldn't attempt to do configuration changes like this using an Orchestration. Technically it's feasible to do what you are attempting to do, but as a practice you shouldn't mix up your business process with administration.

The best way to do such things will be by either writing some normal scripts or PowerShell.

To answer you question, you can get the data you want from BtsOrchestration class in ExplorerOM http://msdn.microsoft.com/en-us/library/microsoft.biztalk.explorerom.btsorchestration_members(v=bts.20)

OTHER TIPS

In order to dynamically configure Dynamic Logical Send Ports from within an orchestration, one has to store the settings into a persistent datastore (e.g. a database or configuration file) and implement a way to assign those properties dynamically at runtime.

But first, we need to understand what is happening when configurating a Dynamic Send Port.

How to Configure a Dynamic Logical Send Port

Configuring the properties of a dynamic logical send port from within an orchestration involves two steps:

  • First, the TransportType and target Address properties must be specified on the Send Port. This is usually done in an Expression Shape with code similar to this:

    DynamicSendPort(Microsoft.XLANGs.BaseTypes.TransportType) = "FILE"; DynamicSendPort(Microsoft.XLANGs.BaseTypes.Address) = "C:\Temp\Folder\%SourceFileName%";

  • Second, any additional transport properties must be specified on the context of the outgoing message itself. Virtually all BizTalk adapters have additional properties that are used for the communication between the Messaging Engine and the XLANG/s Orchestration Engine. For instance, the ReceivedFileName context property is used to dynamically set a specific name for when the FILE adapter will save the outgoing message at its target location. This is best performed inside an Assignment Shape, as part of constructing the outgoing message:

    OutgoingMessage(FILE.ReceiveFileName) = "HardCodedFileName.xml"

You'll notice that most configuration properties must be specified on the context of the outgoing messages, specifying a namespace prefix (e.g. FILE), a property name (e.g. ReceiveFileName) and, obviously, the value that gets assigned to the corresponding property.

In fact, all the context properties are classes that live Inside the well-known Microsoft.BizTalk.GlobalPropertySchemas.dll assembly. This is confirmed by looking up this assembly in Visual Studio's object explorer.

FILE.ReceivedFileName in Microsoft.BizTalk.GlobalPropertySchemas.dll

Even though most context properties that are necessary to configure Dynamic Logical Send Ports live Inside this specific assembly, not all of them do. For instance, the MSMQ BizTalk adapter uses a separate assembly to store its context properties. Obviously, third-party or custom adapters come with additionnal assemblies as well.

Therefore, in order to setup a context property on a Dynamic Send Port using a flexible approach like the one describe below, four pieces of information are necessary:

  • The fully qualified name of the assembly containing the context property classes.
  • The namespace prefix.
  • The property name.
  • The property value.

Storing Port Settings in a Persistent Medium

The following .XSD schema illustrate one possible structure for serializing port settings.

ContextProperties XML Schema Definition

Once serialized, the specified context properties can then be stored in a SQL database or a configuration file very easily. For instance, here are the settings used as an example in this post:

Example of ContextProperties Settings

A Flexible Approach to Configuring Dynamic Logical Send Ports

With a simple helper Library, setting up the dynamic port configuration is very easy. First, you have to retrieve the serialized settings from the persistent medium. This can easily be achieved using the WCF-SQL Adapter and a simple stored procedure.

Once retrieved, those properties can then be deserialized into a strongly-typed C# object graph. For this, first create a C# representation of the ContextProperties schema shown above, using the following command-line utility:

xsd.exe /classes /language:cs /namespace:Helper.Schemas .\ContextProperties.xsd

This generates a partial class that can be improved with the following method:

namespace Helper.Schemas
{
    public partial class ContextProperties
    {
        public static ContextProperties Deserialize(string text)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                byte[] buffer = Encoding.UTF8.GetBytes(text);
                stream.Write(buffer, 0, buffer.Length);
                stream.Seek(0, SeekOrigin.Begin);
                return (ContextProperties) 
                    Deserialize(
                          stream
                        , typeof(ContextProperties));
            }
        }

        public static Object Deserialize(Stream stream, Type type)
        {
            XmlSerializer xmlSerializer = new XmlSerializer(type);
            return xmlSerializer.Deserialize(stream);
        }
    }
}

Second, applying this configuration involves creating an XLANG/s message from code and setting up the context properties dynamically using reflection, based upon the description of the context property classes specified in the deserialized ContextProperties object graph.

For this, I use a technique borrowed from Paolo Salvatori's series of articles regarding dynamic transformations, which consists in creating a custom BTXMessage-derived class, used internally by the BizTalk XLANG/s engine.

namespace Helper.Schemas
{
    using Microsoft.BizTalk.XLANGs.BTXEngine; // Found in Microsoft.XLANGs.BizTalk.Engine
    using Microsoft.XLANGs.Core; // Found in Microsoft.XLANGs.Engine

    [Serializable]
    public sealed class CustomBTXMessage : BTXMessage
    {
        public CustomBTXMessage(string messageName, Context context)
            : base(messageName, context)
        {
            context.RefMessage(this);
        }

        public void SetContextProperty(string assembly, string ns, string name, object value)
        {
            if (String.IsNullOrEmpty(ns))
                ns = "Microsoft.XLANGs.BaseTypes";
            if (String.IsNullOrEmpty(assembly))
                assembly = "Microsoft.BizTalk.GlobalPropertySchemas";

            StringBuilder assemblyQualifiedName = new StringBuilder();
            assemblyQualifiedName.AppendFormat("{0}.{1}, {2}", ns, name, assembly);

            Type type = Type.GetType(assemblyQualifiedName.ToString(), true, true);
            SetContextProperty(type, value);
        }

        internal void SetContextProperty(string property, object value)
        {
            int index = property.IndexOf('.');
            if (index != -1)
                SetContextProperty(String.Empty, property.Substring(0, index), property.Substring(index + 1), value);
            else
                SetContextProperty(String.Empty, String.Empty, property, value);
        }

    }
}

Now, the last piece of the puzzle is how to make use of this custom class from within an Orchestration. This is easily done in an Assignment Shape using the following helper code:

namespace Helper.Schemas
{
    using Microsoft.XLANGs.BaseTypes;
    using Microsoft.XLANGs.Core; // Found in Microsoft.XLANGs.Engine

    public static class Message
    {
        public static XLANGMessage SetContext(XLANGMessage message, ContextProperties properties)
        {
            try
            {
                // create a new XLANGMessage

                CustomBTXMessage customBTXMessage = new CustomBTXMessage(message.Name, Service.RootService.XlangStore.OwningContext);

                // add parts of the original message to it

                for (int index = 0; index < message.Count; index++)
                    customBTXMessage.AddPart(message[index]);

                // set the specified context properties

                foreach (ContextPropertiesContextProperty property in properties.ContextProperty)
                    customBTXMessage.SetContextProperty(property.assembly, property.@namespace, property.name, property.Value);

                return customBTXMessage.GetMessageWrapperForUserCode();
            }

            finally
            {
                message.Dispose();
            }
        }
    }
}

You can use this static method inside your Assignment Shape like the code shown hereafter, where OutboundMessage represents the message which you want to set the context:

OutboundMessage = Helper.Schemas.Message.SetContext(OutboundMessage, contextProperties);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top