BizTalk Pipeline to Fix promotion: AS2ToInternal vs AS2To

Pipeline to fix the odd promotion of AS2To.
See StackOverflow – Why field is promoted as AS2ToInternal instead of AS2To

This C# pipeline code checks for that issues and fixes it by promoting the proper field. I never found out why it was happening and why it needed to be fixed.

<pre>
using System;
using System.Linq;
using System.Xml;
using System.Text;
using System.Collections;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.IO;
using System.Resources;
using System.Reflection;
using System.Drawing;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Component.Utilities;
using Microsoft.XLANGs.RuntimeTypes;
using Microsoft.XLANGs.BaseTypes;

namespace Microsoft.Samples.BizTalk.Pipelines.CustomComponent
{
  /// <summary>
  /// Pipeline component which can't be placed into any receive or send
  /// pipeline stage and do a property promotion / distinguished fields 
  /// writing based on arbitrary XPath.
  /// </summary>
  [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
  [ComponentCategory(CategoryTypes.CATID_Any)]
    [System.Runtime.InteropServices.Guid("EC9794D0-AE8F-42B9-A7E7-02A876998158")]

    public class SetAS2ToPromotedField : 
    BaseCustomTypeDescriptor,
    IBaseComponent, 
    Microsoft.BizTalk.Component.Interop.IComponent, 
    IPersistPropertyBag,
    IComponentUI
  {

        private string _Encoding = "ASCII"; 

        public SetAS2ToPromotedField()
            : base(resourceManager)
    {
    }

    #region Design-time property(ies)

        
        #endregion

    #region IBaseComponent Members

    /// <summary>
    /// Gets a component description.
    /// </summary>
    [Browsable(false)]
    public string Description
    {
            get 
            {
                return "SetAS2To from AS2ToInternal"; 
            }
    }

    /// <summary>
    /// Gets a component name.
    /// </summary>
    [Browsable(false)]
    public string Name
    {
            get 
            {
        return "SetAS2To";
            }
    }

    /// <summary>
    /// Gets a component version.
    /// </summary>
    [Browsable(false)]
    public string Version
    {
      get
      {
        return "1.0";
      }
    }

    #endregion

    #region IComponentUI Members

    /// <summary>
    /// Validate component.
    /// </summary>
    /// <param name="projectSystem">Project system</param>
    /// <returns>Enumerator of error message collection</returns>
    public IEnumerator Validate(object projectSystem)
    {
      // Don't do any validation
      return null;
    }

    /// <summary>
    /// Gets a component icon.
    /// </summary>
    [Browsable(false)]
    public System.IntPtr Icon
    {
      get
      {
        //return ((Bitmap) resourceManager.GetObject("CompIcon")).GetHicon();
                return new System.IntPtr(); 
      }
    }

    #endregion

    #region IComponent Members

    /// <summary>
    /// Executes a pipeline component.
    /// </summary>
    /// <param name="pContext">Pipeline context</param>
    /// <param name="pInMsg">Input message</param>
    /// <returns>Outgoing message</returns>
    public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
    {
      try
      {
        return ExecuteInternal(pContext, pInMsg);
      }
      catch(Exception e)
      {
        // Put component name as a source information in this exception,
        // so the event log in message could reflect this.
        e.Source = Name;
        throw e;
      }
    }

    #endregion

    #region IPersistPropertyBag Members

    /// <summary>
    /// Initialize component.
    /// </summary>
    public void InitNew()
    {
      // Do nothing
    }

    /// <summary>
    /// Get component class ID.
    /// </summary>
    /// <param name="classID"></param>
    public void GetClassID(out Guid classID)
    {
      classID = new Guid("1E7EC223-338E-4D4C-B275-FE9AD90ECE6C");
    }

    /// <summary>
    /// Load component properties from a property bag.
    /// </summary>
    /// <param name="propertyBag">Property bag</param>
    /// <param name="errorLog">Error log level</param>
    public void Load(IPropertyBag propertyBag, int errorLog)
    {
      if ( null == propertyBag )
        throw new ArgumentNullException("propertyBag");

            //string val = (string)ReadPropertyBag(pb, "XPath1");
            //if (val != null) _XPath1 = val;
            string val; 

            //val = (string)ReadPropertyBag(propertyBag, "FilenamePattern");
            //if (val != null) _FilenamePattern = val;
        }

    /// <summary>
    /// Save component properties to a property bag.
    /// </summary>
    /// <param name="propertyBag">Property bag</param>
    /// <param name="clearDirty">Clear dirty flag</param>
    /// <param name="saveAllProperties">Save all properties flag</param>
    public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
    {
      if ( null == propertyBag )
        throw new ArgumentNullException("propertyBag");

            //propertyBag.Write("FilenamePattern", _FilenamePattern);

    }

        #endregion

        #region Private Members

        /// <summary>
        /// Executes the logic for this component.
        /// </summary>
        /// <param name="pContext">Pipeline context</param>
        /// <param name="pInMsg">Input message</param>
        /// <returns>Outgoing message</returns>
        private IBaseMessage ExecuteInternal(IPipelineContext pContext, IBaseMessage pInMsg)
        {
            // Check arguments
            if (null == pContext)
                throw new ArgumentNullException("pContext");

            if (null == pInMsg)
                throw new ArgumentNullException("pInMsg");

            if (null == pInMsg.BodyPart)
                throw new ArgumentNullException("pInMsg.BodyPart");

            if (null == pInMsg.BodyPart.GetOriginalDataStream())
                throw new ArgumentNullException("pInMsg.BodyPart.GetOriginalDataStream()");

            //
            // Create a seekable read-only stream over the input message body part stream.
            //

            // Create a virtual stream, using GetOriginalDataStream() method on the IBaseMessagePart because
            // this call doesn't clone stream (instead of IBaseMessagePart.Data property).
            SeekableReadOnlyStream stream = new SeekableReadOnlyStream(pInMsg.BodyPart.GetOriginalDataStream());

            //
            // Create a new outgoing message, copy all required stuff.
            //

            // Create a new output message
            IBaseMessage outMessage = pContext.GetMessageFactory().CreateMessage();

            // Copy message context by reference
            outMessage.Context = pInMsg.Context;

            // Create new message body part
            IBaseMessagePart newBodyPart = pContext.GetMessageFactory().CreateMessagePart();

            // Copy body part properties by references.
            newBodyPart.PartProperties = pInMsg.BodyPart.PartProperties;

            // Neal added so we can change data in the message itself with replace commands
            StreamReader reader = new StreamReader(stream);
            string text = reader.ReadToEnd();

            byte[] byteArray;
            _Encoding = "ASCII";
            if (_Encoding == "UNICODE")
            {
                byteArray = System.Text.Encoding.Unicode.GetBytes(text);
            }
            else
            {
                byteArray = System.Text.Encoding.ASCII.GetBytes(text);
            }
            MemoryStream memStream = new MemoryStream(byteArray);

            string strAS2Namespace = "http://schemas.microsoft.com/BizTalk/2006/as2-properties";

            object objAS2To = pInMsg.Context.Read("AS2To", strAS2Namespace);
            string strAS2To = objAS2To != null ? objAS2To.ToString() : "";

            object objAS2From = pInMsg.Context.Read("AS2From", strAS2Namespace);
            string strAS2From = objAS2From != null ? objAS2From.ToString() : "";

            // Absolutely no idea why were are seeing the AS2To field promoted as AS2ToInternal. 
            // https://stackoverflow.com/questions/61464238/biztalk-as2-why-field-is-promoted-as-as2tointernal-instead-of-as2to 
            object objAS2ToInternal = pInMsg.Context.Read("AS2ToInternal", strAS2Namespace);
            string strAS2ToInternal = objAS2ToInternal != null ? objAS2ToInternal.ToString() : "";

            object objPreservedFileName = pInMsg.Context.Read("PreservedFileName", strAS2Namespace);
            string strPreservedFileName = objPreservedFileName != null ? objPreservedFileName.ToString() : "";

            string strHttpNamespace = "http://schemas.microsoft.com/BizTalk/2003/http-properties";
            object objHttpHeaders = pInMsg.Context.Read("InboundHttpHeaders", strHttpNamespace);
            string strHttpHeaders = objHttpHeaders != null ? objHttpHeaders.ToString() : "";

            // could not get debugger to work, so had to try Trace 
            string trcGUID = System.Guid.NewGuid().ToString();
            string trcData = "Filename=" + strPreservedFileName;
            string trcXML = "InboundHttpHeaders:" + strHttpHeaders;

            // If No AS2To found, then substitute the next best possible value by promoting it 
            string strNewAS2To = null;
            if (String.IsNullOrEmpty(strAS2To))
            {

                // NOTE: if these two don't work, we could also parse out values from the Httpheader 
                // (but not there, the value is AS2-To 
                if (!String.IsNullOrEmpty(strAS2ToInternal))
                {
                    strNewAS2To = strAS2ToInternal;
                }
                else
                {
                    throw new System.ApplicationException("Could not find any possible value for AS2To. " + 
                        " InboundHttpHeaders=" + strHttpHeaders 
                        );
                }

                // based on above logic, promote a new value to AS2To if needed 
                //pInMsg.Context.Write("AS2To", strAS2Namespace, strNewAS2To);
                pInMsg.Context.Promote("AS2To", strAS2Namespace, strNewAS2To);

            }

            // Set virtual stream as a data stream for the new message body part
            //newBodyPart.Data = stream;
            newBodyPart.Data = memStream;
            pContext.ResourceTracker.AddResource(memStream);

      // Copy message parts
      CopyMessageParts(pInMsg, outMessage, newBodyPart);

            //
            // Return outgoing message.
            //

            return outMessage;
    }

        public static long GetRandomNumberInRange(double minNumber, double maxNumber)
        {
            double dblRandomNumber = new Random().NextDouble() * (maxNumber - minNumber) + minNumber;
            long longRandomNumber = System.Convert.ToInt64(Math.Round(dblRandomNumber, 0));
            return longRandomNumber;
        }

    /// <summary>
    /// Copy message parts from source to destination message.
    /// </summary>
    /// <param name="sourceMessage">Source message</param>
    /// <param name="destinationMessage">Destination message</param>
    /// <param name="newBodyPart">New message body part</param>
    private void CopyMessageParts(IBaseMessage sourceMessage, IBaseMessage destinationMessage, IBaseMessagePart newBodyPart)
    {
      string bodyPartName = sourceMessage.BodyPartName;
      for (int c = 0; c < sourceMessage.PartCount; ++c)
      {
        string partName = null;
        IBaseMessagePart messagePart = sourceMessage.GetPartByIndex(c, out partName);
        if (partName != bodyPartName)
        {
          destinationMessage.AddPart(partName, messagePart, false);
        }
        else
        {
          destinationMessage.AddPart(bodyPartName, newBodyPart, true);
        }
      }
    }

    /// <summary>
    /// Gets a message type from the XML stream.
    /// </summary>
    /// <param name="stream">Seekable stream</param>
    /// <returns>Message type</returns>
    /// <exception cref="InvalidOperationException">If message type can't be determined</exception>
    private string GetMessageType(Stream stream)
    {
      // Fail if message stream is not seekable
      if (!stream.CanSeek)
        throw new InvalidOperationException("An attempt to get a message type for non-seekable stream");

      // Save the current stream position
      long position = stream.Position;
      try
      {
        // Create XmlTextReader over the message stream
        XmlTextReader reader = new XmlTextReader(stream);
        while (reader.Read())
        {
          // Check if current node in XmlTextReader is element and it's global
          if (XmlNodeType.Element == reader.NodeType && 0 == reader.Depth)
          {
            // Found a global element, now build message type as <namespaceURI>#<localName> 
            // if namespaceURI is not empty and <localName> if namespaceURI is empty.
            if (reader.NamespaceURI != null && reader.NamespaceURI.Length > 0)
            {
              return reader.NamespaceURI + '#' + reader.LocalName;
            }
            else
            {
              return reader.LocalName;
            }
          }
        }

        // Not found, fail
        throw new InvalidOperationException("Message type can't be determined");
      }
      finally
      {
        // Restore the stream position
        stream.Position = position;
      }
    }

    /// <summary>
    /// Read property bag in order to eat the argument exception which is thrown
    /// when properties are not populated.
    /// </summary>
    /// <param name="propertyBag">Property bag</param>
    /// <param name="propertyName">Property name</param>
    /// <returns>Property value</returns>
    private object ReadPropertyBag(IPropertyBag propertyBag, string propertyName)
    {
      object value;
      try
      {
        propertyBag.Read(propertyName, out value, 0);
        return value;
      }
      catch (ArgumentException)
      {
        // IPropertyBag.Read throws an ArgumentException if there are no properties in there.
        // Just return null in this case.
        return null;
      }
    }

    private SchemaList documentSchemaList = new SchemaList();

    private static PromotingMap promotingMap = new PromotingMap();
    private static ResourceManager resourceManager = new ResourceManager("Microsoft.Samples.BizTalk.Pipelines.CustomComponent.ArbitraryXPathPropertyHandler", Assembly.GetExecutingAssembly());
    private static PropertyBase messageTypeWrapper = new BTS.MessageType();

    //private const string DocumentSpecNamesPropertyName = "DocumentSpecNames";
    //private const string DocumentSpecNamespacesPropertyName = "DocumentSpecNamespaces";
    //private const char   DocumentSpecNamesSeparator = '|';
    
    #endregion
  }
}
</pre>

Uncategorized  

Leave a Reply