Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>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.</p> <p>But first, we need to understand what is happening when configurating a Dynamic Send Port.</p> <p><strong>How to Configure a Dynamic Logical Send Port</strong></p> <p><a href="http://msdn.microsoft.com/en-us/library/cc185228%28v=BTS.10%29.aspx" rel="nofollow noreferrer">Configuring the properties of a dynamic logical send port</a> from within an orchestration involves two steps:</p> <ul> <li><p>First, the <a href="http://msdn.microsoft.com/en-us/library/microsoft.xlangs.basetypes.transporttype%28v=bts.20%29" rel="nofollow noreferrer">TransportType</a> and target <a href="http://msdn.microsoft.com/en-us/library/microsoft.xlangs.basetypes.address(v=BTS.20).aspx" rel="nofollow noreferrer">Address</a> properties must be specified on the Send Port. This is usually done in an <em>Expression Shape</em> with code similar to this:</p> <p>DynamicSendPort(Microsoft.XLANGs.BaseTypes.TransportType) = "FILE"; DynamicSendPort(Microsoft.XLANGs.BaseTypes.Address) = "C:\Temp\Folder\%SourceFileName%";</p></li> <li><p>Second, any additional transport properties must be specified on the context of the outgoing message itself. Virtually all BizTalk adapters have <a href="http://msdn.microsoft.com/en-us/library/aa562116%28v=bts.20%29.aspx" rel="nofollow noreferrer">additional properties</a> that are used for the communication between the Messaging Engine and the XLANG/s Orchestration Engine. For instance, the <a href="http://msdn.microsoft.com/en-us/library/file.receivedfilename%28v=bts.10%29.aspx" rel="nofollow noreferrer">ReceivedFileName</a> 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 <em>Assignment Shape</em>, as part of constructing the outgoing message:</p> <p>OutgoingMessage(FILE.ReceiveFileName) = "HardCodedFileName.xml"</p></li> </ul> <p>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.</p> <p>In fact, all the context properties are classes that live Inside the <em>well-known</em> <code>Microsoft.BizTalk.GlobalPropertySchemas.dll</code> assembly. This is confirmed by looking up this assembly in Visual Studio's object explorer.</p> <p><img src="https://i.stack.imgur.com/EgIvr.png" alt="FILE.ReceivedFileName in Microsoft.BizTalk.GlobalPropertySchemas.dll"></p> <p>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.</p> <p>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:</p> <ul> <li>The fully qualified name of the assembly containing the context property classes.</li> <li>The namespace prefix.</li> <li>The property name.</li> <li>The property value.</li> </ul> <p><strong>Storing Port Settings in a Persistent Medium</strong></p> <p>The following .XSD schema illustrate one possible structure for serializing port settings.</p> <p><img src="https://i.stack.imgur.com/JSWrM.png" alt="ContextProperties XML Schema Definition"></p> <p>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:</p> <p><img src="https://i.stack.imgur.com/WkRdW.png" alt="Example of ContextProperties Settings"></p> <p><strong>A Flexible Approach to Configuring Dynamic Logical Send Ports</strong></p> <p>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.</p> <p>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:</p> <pre><code>xsd.exe /classes /language:cs /namespace:Helper.Schemas .\ContextProperties.xsd </code></pre> <p>This generates a partial class that can be improved with the following method:</p> <pre><code>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); } } } </code></pre> <p>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.</p> <p>For this, I use a technique borrowed from <a href="http://blogs.msdn.com/b/paolos/" rel="nofollow noreferrer">Paolo Salvatori</a>'s series of articles <a href="http://blogs.msdn.com/b/paolos/archive/2010/01/29/how-to-boost-message-transformations-using-the-xslcompiledtransform-class.aspx" rel="nofollow noreferrer">regarding dynamic transformations</a>, which consists in creating a custom <a href="http://msdn.microsoft.com/en-us/library/microsoft.biztalk.xlangs.btxengine.btxmessage%28v=BTS.10%29.aspx" rel="nofollow noreferrer">BTXMessage</a>-derived class, used internally by the BizTalk XLANG/s engine.</p> <pre><code>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); } } } </code></pre> <p>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 <em>Assignment Shape</em> using the following helper code:</p> <pre><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 &lt; 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(); } } } } </code></pre> <p>You can use this static method inside your <em>Assignment Shape</em> like the code shown hereafter, where <code>OutboundMessage</code> represents the message which you want to set the context:</p> <pre><code>OutboundMessage = Helper.Schemas.Message.SetContext(OutboundMessage, contextProperties); </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload