Showing posts with label BizTalk. Show all posts
Showing posts with label BizTalk. Show all posts

Thursday, November 2, 2017

Read Promoted properties in Orchestration from XmlDocument Message


When the PropertySchemaBase property is set to MessageDataPropertyBase, it means that the value of the promoted property corresponds to data in the message, such as the value of some field. When the PropertySchemaBase attribute is set to MessageContextPropertyBase, it means that the value of the promoted property may be from somewhere else, such as an envelope, or that it may be set by a pipeline component.


So, change the PropertySchemaBase property to MessageDataPropertyBase for all Promoted fields in Property schema.

We can bale to read the promoted property value by assigning the untyped document (XmlDocument) to XLANGMessage.

Declare the varXlangMessage is an orchestration variable of type Microsoft.XLANGs.BaseTypes.XLANGMessage. Use below code to read the property value.

varXlangMessage= msgXmlDocument;
varValue = System.Convert.ToString(varXlangMessage.GetPropertyValue(typeof({YourPropertySchema}.{PromtedProperty})));

Subscribe for Routing failure on Delivery Notifications (NACK messages)

I could not found exact solution for this, i followed below work-around, but it won’t solve all of the problems and side effects. The idea is to create an orchestration which subscribes to all NACK acknowledgments. That is to say:
·         The message type of the incoming message will be XmlDocument
·         The BTS.AckType property == NACK
·         The logical receive port will use direct binding
By doing so, all acknowledgments will be consumed by an instance of this orchestration, thus avoiding the routing failure. 



Friday, July 8, 2016

"Keyset does not exist" error message when you try to change the identity of an application pool by using Internet Information Services


When I try to change the identity of any application pool, received the following error message:

---------------------------
Application Pools
---------------------------
There was an error while performing this operation.

Details:

Keyset does not exist (Exception from HRESULT: 0x80090016)


Reason : 
         There is not enough permissions to open file

  • %ALLUSERSPROFILE%\Microsoft\Crypto\RSA\MachineKeys\ 6de9cb26d2b98c01ec4e9e8b34824aa2_*
  • %ALLUSERSPROFILE%\Microsoft\Crypto\RSA\MachineKeys\ 76944fb33636aeddb9590521c2e8815a_*


Resolution

  1.     Go to '%ALLUSERSPROFILE%\Microsoft\Crypto\RSA\MachineKeys'
  2.     Find files with name starts as 6de9cb26d2b98c01ec4e9e8b34824aa2_ and                            76944fb33636aeddb9590521c2e8815a_.
  3.    Check permission for this file. Default permissions are:
    •        System, Administrators,TrustedInstaller - Full permissions
    •       IIS_IUSR,WMSVC - Read permissions
    •       LOCAL SERVICE - Read permission

If the issue still persists:

Take backup of MachineKeys (%ALLUSERSPROFILE%\Microsoft\Crypto\RSA\MachineKeys)
Give full permissions to your account to access the MachineKeys folder.

  Please check that files have name 6de9cb26d2b98c01ec4e9e8b34824aa2_GUID and 76944fb33636aeddb9590521c2e8815a_GUID .

If not, just copy existing files started with name 6de9cb26d2b98c01ec4e9e8b34824aa2_ and 76944fb33636aeddb9590521c2e8815a_ and then set GUID part in the file name equals to GUID obtained from MachineGuid registry key from HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryprography\





Monday, January 11, 2016

Why BizTalk not supporting multiple send locations for one send port?

     We can have multiple disassemblers in Receive Pipeline, we can receive multiple format files from the same receive pipeline. It execute all the components (0 to 255)placed in it.

     But we cannot have multiple assemblers in Send Pipeline (it can have only 0 to 1). So, we can't use  to send multiple format files through the same Send pipeline.


What if we  want to send multiple format files through the same Send pipeline ?
    We can create a Send port for each format and add them to a Send Port Group

Even I too have this below question in my mind, any idea!

Why BizTalk doesn't provide this feature, BizTalk server is not supporting more than one send location for one send port?

Wednesday, May 27, 2015

Alternative to use String.Split function in BizTalk Orchstration

My requirement is to get the 6th ISA segment value.

The var_ISASegment contains this value "ISA*00*          *00*          *01*FIRST *01*98765LCSAPT*090318*1420*U*00401*000442082*0*T*^~"

Solution:
We will have to use an expression shape and a loop and Expression shape


Place this below condition in the loop
var_counter < 8


 create 3 variables (var_PrvPosition, var_curntPosition, var_counter) with int data type.  

  • var_PrvPosition used to store the startup position.  
  • var_curntPosition used to store  index of our delimiter 
  • var_counter used to store the segment number.


Place below code in the expression shape
var_PrvPosition ++;
var_curntPosition = var_ISASegment.IndexOf("*",var_PrvPosition);
               
var_counter++;
if (counter == 7)
{
  Var_SixthSegment = var_ISASegment.Substring(var_PrvPosition, var_curntPositionvar_PrvPosition));
}

var_PrvPosition = indexCurrentPosition;


Tuesday, April 7, 2015

Debatching(Splitting) XML Message in Orchestration using Custom Pipeline with specified BatchSize



I have used custom pipeline component to debatch it which uses XML disassembler.

“Disassemble” is the second stage of the receive pipeline which is primarily used to disassemble incoming messages, validate schema and promote properties. To disassemble (break) messages it uses an envelope schema and it breaks the message record-wise. There is no provision to break a set of records (batches).


Sample Code:

1. Create a class library project.
2. Add reference to below assemblies
         a). Microsoft.XLANGs.BaseTypes (C:\Program Files (x86)\Microsoft BizTalk Server 2010\Microsoft.XLANGs.BaseTypes.dll)
         b). Microsoft.BizTalk.Pipeline (C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies\Microsoft.BizTalk.Pipeline.dll)
3. Rename default class1.cls to XMLDebatch.cs.
4. Use following code




[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [ComponentCategory(CategoryTypes.CATID_DisassemblingParser)]
    [System.Runtime.InteropServices.Guid("6cf0de2b-fc7a-4a8d-913b-9d48d59ca584")]
    public class XMLDebatch : IBaseComponent, IDisassemblerComponent, IComponentUI, IPersistPropertyBag
    {
        #region Private Variables

        private ResourceManager resourceManager = null;

        //Used to hold disassembled messages
        private System.Collections.Queue qOutputMsgs = null;
        private string systemPropertiesNamespace = null;
        private string filePropertiesNamespace = null;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="XMLDebatch"/> class.
        /// </summary>
        public XMLDebatch()
        {
            resourceManager = new ResourceManager("{YourClassLibrary}.XMLDebatch", Assembly.GetExecutingAssembly());
            qOutputMsgs = new System.Collections.Queue();
            systemPropertiesNamespace = @"http://schemas.microsoft.com/BizTalk/2003/system-properties";
            filePropertiesNamespace = "http://schemas.microsoft.com/BizTalk/2003/file-properties";

        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the size of the batch.
        /// </summary>
        /// <value>
        /// The size of the batch.
        /// </value>
        [DisplayName("BatchSize")]
        [DefaultValue(50)]
        public int BatchSize
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the batch root element x path.
        /// </summary>
        /// <value>
        /// The batch root element x path.
        /// </value>
        [DisplayName("BatchRootElementXPath")]
        public string BatchRootElementXPath
        {
            get;
            set;
        }

        #endregion

        #region IBaseComponent

        /// <summary>
        /// Description of the Component
        /// </summary>
        public string Description
        {
            get { return resourceManager.GetString("COMPONENTDESCRIPTION", CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Name of the Component
        /// </summary>
        public string Name
        {
            get { return resourceManager.GetString("COMPONENTNAME", CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Version of the Component
        /// </summary>
        public string Version
        {
            get { return resourceManager.GetString("COMPONENTVERSION", CultureInfo.InvariantCulture); }
        }

        #endregion

        #region IPersistPropertyBag

        /// <summary>
        /// GetClassID of component for usage from unmanaged code.
        /// </summary>
        /// <param name="classID"></param>
        public void GetClassID(out Guid classID)
        {
            classID = new Guid("0713faf8-748c-44d3-a1b1-8a80e3b168fc");
        }

        /// <summary>
        /// InitNew
        /// </summary>
        public void InitNew()
        {
        }

        /// <summary>
        /// Loads configuration properties for the component
        /// </summary>
        /// <param name="propertyBag"></param>
        /// <param name="errorLog"></param>
        public void Load(IPropertyBag propertyBag, int errorLog)
        {
            try
            {
                BatchSize = ReadPropertyBag<int>(propertyBag, "BatchSize");
                BatchRootElementXPath = ReadPropertyBag<string>(propertyBag, "BatchRootElementXPath");
            }
            catch (NullReferenceException ex)
            {
                System.Diagnostics.EventLog.WriteEntry("Error in reading property bag", ex.Message);
                throw ex;
            }
        }

        /// <summary>
        /// Saves the current component configuration into the property bag
        /// </summary>
        /// <param name="pb">Configuration property bag</param>
        /// <param name="fClearDirty">not used</param>
        /// <param name="fSaveAllProperties">not used</param>
        public virtual void Save(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, bool fClearDirty, bool fSaveAllProperties)
        {
            WritePropertyBag(pb, "BatchSize", this.BatchSize);
            WritePropertyBag(pb, "BatchRootElementXPath", this.BatchRootElementXPath);
        }

        #endregion

        #region IComponentUI

        /// <summary>
        /// Component icon to use in BizTalk Editor
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public IntPtr Icon
        {
            get
            {
                return ((System.Drawing.Bitmap)(this.resourceManager.GetObject("COMPONENTICON", System.Globalization.CultureInfo.InvariantCulture))).GetHicon();
            }
        }

        /// <summary>
        /// The Validate method is called by the BizTalk Editor during the build of a BizTalk project.
        /// </summary>
        /// <param name="projectSystem"></param>
        /// <returns></returns>
        public System.Collections.IEnumerator Validate(object projectSystem)
        {
            return null;
        }

        #endregion

        #region IDisassemblerComponent

        /// <summary>
        /// Disassembles the specified p context.
        /// </summary>
        /// <param name="pContext">The p context.</param>
        /// <param name="pInMsg">The pInMSG.</param>
        /// <exception cref="System.ApplicationException">
        /// Error in reading original message:  + ex.Message
        /// or
        /// Error in writing outgoing messages:  + ex.Message
        /// </exception>
        public void Disassemble(IPipelineContext pContext, Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
        {
            XPathNavigator xPathNavigator = null;
            XmlNamespaceManager namespaceManager = null;
            XPathNodeIterator nodeIterator = null;
            string receivedFileName = pInMsg.Context.Read("ReceivedFileName", filePropertiesNamespace).ToString();
            string fileName = System.IO.Path.GetFileNameWithoutExtension(receivedFileName);
            long enrollmentsCount = 0;
            bool isOutstandingChunks = true;

            try
            {
                //fetch original message
                Stream originalMessageStream = pInMsg.BodyPart.GetOriginalDataStream();
                XPathDocument document = new XPathDocument(originalMessageStream);
                xPathNavigator = document.CreateNavigator();
                namespaceManager = XmlDocumentHelper.GetNameSpacesFromMessage(document);
                enrollmentsCount = long.Parse(xPathNavigator.Evaluate(string.Concat("count(", BatchRootElementXPath, "/*)"), namespaceManager).ToString());
            }

            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in reading original message: ", ex.Message));
                throw new ApplicationException(string.Concat("Error in reading original message: ", ex.Message));
            }

         
            try
            {
                long startPosition = 1;
                long FinalPosition = BatchSize;
                string[] nodes = null;
                string finalChunkString = null;
                XmlDocument chunkDoc = null;
                XmlNode batchRootElement = null;
                int batchCount = 0;
                string batchId = Guid.NewGuid().ToString();
                XmlDocument templateDoc = new XmlDocument();

                templateDoc.LoadXml(xPathNavigator.OuterXml);
                batchRootElement = templateDoc.SelectSingleNode(BatchRootElementXPath, namespaceManager);
                batchRootElement.InnerXml = string.Empty;   //Clear children.

                while (isOutstandingChunks)
                {
                    chunkDoc = templateDoc;
                    batchRootElement = chunkDoc.SelectSingleNode(BatchRootElementXPath, namespaceManager);
                    string namespaceURI = chunkDoc.DocumentElement.NamespaceURI;
                    string rootElement = chunkDoc.DocumentElement.LocalName;


                    nodeIterator = xPathNavigator.Select(string.Concat(BatchRootElementXPath, string.Format("/*[position() >= {0} and position() <= {1}]", startPosition, FinalPosition)), namespaceManager);
                    nodes = nodeIterator.Cast<XPathNavigator>().Select(item => item.OuterXml).ToArray();
                    finalChunkString = string.Join(string.Empty, nodes);

                    batchRootElement.InnerXml = finalChunkString;

                    batchCount++;
                    CreateOutgoingMessage(pContext, chunkDoc.OuterXml, namespaceURI, rootElement, pInMsg, batchCount, batchId);
                    startPosition = FinalPosition + 1;
                    if (FinalPosition >= enrollmentsCount)
                    {
                        isOutstandingChunks = false;
                    }
                    else
                    {
                        FinalPosition = FinalPosition + BatchSize;
                        if (FinalPosition > enrollmentsCount)
                        {
                            FinalPosition = enrollmentsCount;
                        }
                    }

                }

                batchRootElement.InnerXml = string.Empty;

            }
            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in writing outgoing messages: ", ex.Message));
                throw new ApplicationException("Error in writing outgoing messages: " + ex.Message);
            }
        }

        /// <summary>
        /// Gets the next message.
        /// </summary>
        /// <param name="pContext">The pContext.</param>
        /// <returns></returns>
        public Microsoft.BizTalk.Message.Interop.IBaseMessage GetNext(IPipelineContext pContext)
        {
            IBaseMessage returnMessage = null;

            if (qOutputMsgs.Count > 0)
            {
                returnMessage = (IBaseMessage)qOutputMsgs.Dequeue();
            }

            return returnMessage;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Reads the property bag.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyBag">The property bag.</param>
        /// <param name="propName">Name of the property.</param>
        /// <returns></returns>
        private T ReadPropertyBag<T>(IPropertyBag propertyBag, string propName)
        {
            T returnValue = default(T);
            object val = null;
            try
            {
                propertyBag.Read(propName, out val, 0);
                val = val ?? default(T);
                returnValue = (T)Convert.ChangeType(val, typeof(T));
            }
            catch (System.ArgumentException)
            {
                return returnValue;
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in reading into property bag", ex.Message));
                throw new ApplicationException(string.Concat("Error in reading into property bag: ", ex.Message));
            }
            return returnValue;
        }

        /// <summary>
        /// Writes the property bag.
        /// </summary>
        /// <param name="propertyBag">The property bag.</param>
        /// <param name="propName">Name of the property.</param>
        /// <param name="val">The value.</param>
        private void WritePropertyBag(IPropertyBag propertyBag, string propName, object val)
        {
            try
            {
                propertyBag.Write(propName, ref val);
            }
            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("Error in writing into property bag", ex.Message);
                throw new ApplicationException(string.Concat("Error in writing into property bag: ", ex.Message));
            }
        }

        /// <summary>
        /// Queue outgoing messages
        /// </summary>
        private void CreateOutgoingMessage(IPipelineContext pContext, String messageString, string namespaceURI, string rootElement, IBaseMessage pInMsg, int batchIndex, string batchId)
        {

            IBaseMessage outMsg = null;
            try
            {
                string receivedFileName = pInMsg.Context.Read("ReceivedFileName", filePropertiesNamespace).ToString();
                string fileName = System.IO.Path.GetFileNameWithoutExtension(receivedFileName);
                string[] fileNameAttr = fileName.Split(new char[] { '.' });

                fileName = fileNameAttr[fileNameAttr.Length - 1];
                receivedFileName = Path.GetDirectoryName(receivedFileName);
                //create outgoing message
                outMsg = pContext.GetMessageFactory().CreateMessage();
                outMsg.AddPart("Body", pContext.GetMessageFactory().CreateMessagePart(), true);
                outMsg.Context = PipelineUtil.CloneMessageContext(pInMsg.Context);
                outMsg.Context.Promote("MessageType", systemPropertiesNamespace, string.Concat(namespaceURI, "#", rootElement));
                outMsg.Context.Promote("ReceivedFileName", filePropertiesNamespace, string.Concat(receivedFileName, "\\", fileName, ".", batchId, ".Chunk.", batchIndex.ToString("00"), ".xml"));
                byte[] bufferOoutgoingMessage = System.Text.ASCIIEncoding.ASCII.GetBytes(messageString);
                outMsg.BodyPart.Data = new MemoryStream(bufferOoutgoingMessage);
                qOutputMsgs.Enqueue(outMsg);

            }
            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in queening outgoing messages:", ex.Message));
                throw new ApplicationException(string.Concat("Error in queening outgoing messages: ", ex.Message));
            }
        }

        #endregion

    }


Create new XmlDocumentHelper class and use the below code.

public class XmlDocumentHelper
    {
        #region Private Variables

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="XmlDocumentHelper"/> class.
        /// </summary>
        public XmlDocumentHelper()
        {
        }

        #endregion

        #region Private Methods

        #endregion

        #region Public Methods

        /// <summary>
        /// Constructs the empty message.
        /// </summary>
        /// <param name="objType">Type of the object.</param>
        /// <param name="rootNodeName">Name of the root node.</param>
        /// <returns></returns>
        public static XmlDocument ConstructEmptyMessage(Type objType, string rootNodeName = "")
        {
            Type MessageSchemaType = objType;

            try
            {
                DocumentSpec DocumentSpecification = null;
                if (string.IsNullOrEmpty(rootNodeName))
                {
                    DocumentSpecification = new DocumentSpec(MessageSchemaType.FullName, MessageSchemaType.Assembly.FullName);
                }
                else
                {
                    DocumentSpecification = new DocumentSpec(string.Concat(MessageSchemaType.FullName, "+", rootNodeName), MessageSchemaType.Assembly.FullName);
                }

                XmlDocument SchemaInstance = new XmlDocument();
                using (StreamReader InstanceStreamReader = new StreamReader(DocumentSpecification.GetDocSchema().CreateXmlInstance()))
                {
                    SchemaInstance.Load(InstanceStreamReader);
                }

                return SchemaInstance;

            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        /// <summary>
        /// Gets the name spaces from message.
        /// </summary>
        /// <param name="doc">The document.</param>
        /// <returns></returns>
        public static XmlNamespaceManager GetNameSpacesFromMessage(XPathDocument xDocument)
        {
            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(new NameTable());
            XPathNavigator nav = xDocument.CreateNavigator();
            XPathNodeIterator nodes = (XPathNodeIterator)nav.Evaluate("//namespace::*");

            while (nodes.MoveNext())
            {
                namespaceManager.AddNamespace(nodes.Current.Name, nodes.Current.Value);
            }


            return namespaceManager;
        }

        /// <summary>
        /// Sets the node value.
        /// </summary>
        /// <param name="doc">The document.</param>
        /// <param name="xpath">The xpath.</param>
        /// <param name="value">The value.</param>
        /// <param name="namespaceManager">The namespace manager.</param>
        public static void SetNodeValue(XmlDocument doc, string xpath, string value, XmlNamespaceManager namespaceManager)
        {
            if (doc != null && !string.IsNullOrEmpty(xpath))
            {
                string nodeValue = string.Empty;
                XmlNode xpathNode = doc.SelectSingleNode(xpath, namespaceManager);
                if (xpathNode != null)
                {
                    xpathNode.InnerText = value;
                }
            }
        }

        #endregion
    }

Monday, December 1, 2014

Execute XSLT from pipeline component

In few cases we don't have to actually create a map for transforming the message and this custom pipeline component used to transform an XML message using XSLT.

Sample for suppress the blank nodes before message going to final destination.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template match="@*|node()"><xsl:if test=". != ''"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:if></xsl:template></xsl:stylesheet>

Pipeline component:
[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [System.Runtime.InteropServices.Guid("34b2fdfe-0c6f-4e6a-992a-511058a2c467")]
    [ComponentCategory(CategoryTypes.CATID_Encoder)]
    public class TransformMessage : IBaseComponent, Microsoft.BizTalk.Component.Interop.IComponent, IComponentUI, IPersistPropertyBag
    {
        #region Private Variables

        private ResourceManager resourceManager = null;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="Class1"/> class.
        /// </summary>
        public TransformMessage()
        {
            resourceManager = new ResourceManager("My.Common.BizTalk.PipelineComponent.XSLTtransform.TransformMessage", Assembly.GetExecutingAssembly());
        }

        #endregion

        #region Public properties

        /// <summary>
        /// Gets or sets the custom XSLT.
        /// </summary>
        /// <value>
        /// The custom XSLT.
        /// </value>
        [DisplayName("CustomXSLT")]
        public string CustomXSLT
        {
            get;
            set;
        }

        #endregion

        #region IBaseComponent

        /// <summary>
        /// Description of the Component
        /// </summary>
        public string Description
        {
            get { return resourceManager.GetString("COMPONENTDESCRIPTION", CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Name of the Component
        /// </summary>
        public string Name
        {
            get { return resourceManager.GetString("COMPONENTNAME", CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Version of the Component
        /// </summary>
        public string Version
        {
            get { return resourceManager.GetString("COMPONENTVERSION", CultureInfo.InvariantCulture); }
        }

        #endregion

        #region IPersistPropertyBag

        /// <summary>
        /// GetClassID of component for usage from unmanaged code.
        /// </summary>
        /// <param name="classID"></param>
        public void GetClassID(out Guid classID)
        {
            classID = new Guid("00b3e605-1cbe-41ff-bb43-4bad07f2f3ef");
        }

        /// <summary>
        /// InitNew
        /// </summary>
        public void InitNew()
        {
        }

        /// <summary>
        /// Loads configuration properties for the component
        /// </summary>
        /// <param name="propertyBag"></param>
        /// <param name="errorLog"></param>
        public void Load(IPropertyBag propertyBag, int errorLog)
        {
            try
            {
                CustomXSLT = ReadPropertyBag<string>(propertyBag, "CustomXSLT");
            }
            catch (NullReferenceException ex)
            {
                System.Diagnostics.EventLog.WriteEntry("Error in reading property bag", ex.Message);
                throw ex;
            }
        }

        /// <summary>
        /// Saves the current component configuration into the property bag
        /// </summary>
        /// <param name="pb">Configuration property bag</param>
        /// <param name="fClearDirty">not used</param>
        /// <param name="fSaveAllProperties">not used</param>
        public virtual void Save(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, bool fClearDirty, bool fSaveAllProperties)
        {
            WritePropertyBag(pb, "CustomXSLT", this.CustomXSLT);
        }

        #endregion

        #region IComponentUI

        /// <summary>
        /// Component icon to use in BizTalk Editor
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public IntPtr Icon
        {
            get
            {
                return ((System.Drawing.Bitmap)(this.resourceManager.GetObject("COMPONENTICON", System.Globalization.CultureInfo.InvariantCulture))).GetHicon();
            }
        }

        /// <summary>
        /// The Validate method is called by the BizTalk Editor during the build of a BizTalk project.
        /// </summary>
        /// <param name="projectSystem"></param>
        /// <returns></returns>
        public System.Collections.IEnumerator Validate(object projectSystem)
        {
            return null;
        }

        #endregion

        #region IComponent

        /// <summary>
        /// Execute Method
        /// </summary>
        /// <param name="pContext"></param>
        /// <param name="pInMsg"></param>
        /// <returns></returns>
        public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(IPipelineContext pContext, Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
        {
            Stream vStream = new VirtualStream(pInMsg.BodyPart.GetOriginalDataStream());
            ReadOnlySeekableStream message = new ReadOnlySeekableStream(vStream);
            MemoryStream memoryStream = new MemoryStream();
            message.CopyTo(memoryStream);
            message.Position = 0;

            XPathDocument xPathDoc = new XPathDocument(message);
            XslCompiledTransform transform = new XslCompiledTransform();
            MemoryStream xslStream = new MemoryStream();
            byte[] outBytes = System.Text.Encoding.ASCII.GetBytes(CustomXSLT);

            xslStream.Write(outBytes, 0, outBytes.Length);
            xslStream.Position = 0;
            XPathDocument xsltDoc = new XPathDocument((Stream)xslStream);
            transform.Load(xsltDoc);

            //transform.Load(
            memoryStream = new MemoryStream();
            transform.Transform(xPathDoc, null, memoryStream);
            memoryStream.Seek(0, SeekOrigin.Begin);

            pInMsg.BodyPart.Data = memoryStream;
            return pInMsg;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Reads the property bag.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyBag">The property bag.</param>
        /// <param name="propName">Name of the property.</param>
        /// <returns></returns>
        private T ReadPropertyBag<T>(IPropertyBag propertyBag, string propName)
        {
            T returnValue = default(T);
            object val = null;
            try
            {
                propertyBag.Read(propName, out val, 0);
                returnValue = (T)Convert.ChangeType(val, typeof(T));
            }
            catch (System.ArgumentException)
            {
                return returnValue;
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in reading into property bag", ex.Message));
                throw new ApplicationException(string.Concat("Error in reading into property bag: ", ex.Message));
            }
            return returnValue;
        }

        /// <summary>
        /// Writes the property bag.
        /// </summary>
        /// <param name="propertyBag">The property bag.</param>
        /// <param name="propName">Name of the property.</param>
        /// <param name="val">The value.</param>
        private void WritePropertyBag(IPropertyBag propertyBag, string propName, object val)
        {
            try
            {
                propertyBag.Write(propName, ref val);
            }
            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("Error in writing into property bag", ex.Message);
                throw new ApplicationException(string.Concat("Error in writing into property bag: ", ex.Message));
            }
        }

        #endregion
    }

Tuesday, June 17, 2014

Custom property is not visible for the pipeline in the BizTalk Admin Console


Unregister your pipeline component from Visual Studio tool-box.
Un-Deploy from GAC your pipeline component assembly.
Delete the your pipeline component from the <BizTalk Install Folder>\Pipeline Components directory.
Close all instances of Visual Studio.
Open your Custom Pipeline Component solution and rebuild.
Copy your Custom Pipeline Component assembly to <BizTalk Install Folder>\Pipeline Components.
GAC your custom pipeline component assembly.
Register your custom pipeline component in Visual Studio.
Try to drag your pipeline component to the pipeline.

Monday, June 16, 2014

Custom Receive Pipeline Component to break Message into Batches for Group elements

“Disassemble” is the second stage of the receive pipeline which is primarily used to disassemble incoming messages, validate schema and promote properties. To disassemble (break) messages it uses an envelope schema and it breaks the message record-wise. There is no provision to break a set of records (batches).


Sample Code:

1. Create a class library project.
2. Add reference to Microsoft.BizTalk.Pipeline.dll.
3. Rename default class1.cls to XMLSplitter.cs.
4. Use following code


[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [ComponentCategory(CategoryTypes.CATID_DisassemblingParser)]
    [System.Runtime.InteropServices.Guid("b70b07ec-3b6c-475d-a5da-eb2ad5ea5c28")]
    public class XMLSplitter : IBaseComponent, IDisassemblerComponent, IComponentUI, IPersistPropertyBag
    {
        #region Private Variables

        private ResourceManager resourceManager = null;
     
        //Used to hold disassembled messages
        private System.Collections.Queue qOutputMsgs = null;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="XMLSplitter"/> class.
        /// </summary>
        public XMLSplitter()
        {
            resourceManager = new ResourceManager("PipelineComponent.XMLDebatch.XMLSplitter", Assembly.GetExecutingAssembly());
            qOutputMsgs = new System.Collections.Queue();

            BatchSize = 50;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the size of the batch.
        /// </summary>
        /// <value>
        /// The size of the batch.
        /// </value>
        public int BatchSize
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the batch root element x path.
        /// </summary>
        /// <value>
        /// The batch root element x path.
        /// </value>
        public string BatchRootElementXPath
        {
            get;
            set;
        }

        #endregion

        #region IBaseComponent

        /// <summary>
        /// Description of the Component
        /// </summary>
        public string Description
        {
            get { return resourceManager.GetString("COMPONENTDESCRIPTION", CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Name of the Component
        /// </summary>
        public string Name
        {
            get { return resourceManager.GetString("COMPONENTNAME", CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Version of the Component
        /// </summary>
        public string Version
        {
            get { return resourceManager.GetString("COMPONENTVERSION", CultureInfo.InvariantCulture); }
        }

        #endregion

        #region IPersistPropertyBag

        /// <summary>
        /// GetClassID of component for usage from unmanaged code.
        /// </summary>
        /// <param name="classID"></param>
        public void GetClassID(out Guid classID)
        {
            classID = new Guid("0713faf8-748c-44d3-a1b1-8a80e3b168fc");
        }

        /// <summary>
        /// InitNew
        /// </summary>
        public void InitNew()
        {
        }

        /// <summary>
        /// Loads configuration properties for the component
        /// </summary>
        /// <param name="propertyBag"></param>
        /// <param name="errorLog"></param>
        public void Load(IPropertyBag propertyBag, int errorLog)
        {
            try
            {
                BatchSize = ReadPropertyBag<int>(propertyBag, "BatchSize");
                BatchRootElementXPath = ReadPropertyBag<string>(propertyBag, "BatchRootElementXPath");
            }
            catch (NullReferenceException ex)
            {
                System.Diagnostics.EventLog.WriteEntry("Error in reading property bag", ex.Message);
                throw ex;
            }
        }

        /// <summary>
        /// Saves the current component configuration into the property bag
        /// </summary>
        /// <param name="pb">Configuration property bag</param>
        /// <param name="fClearDirty">not used</param>
        /// <param name="fSaveAllProperties">not used</param>
        public virtual void Save(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, bool fClearDirty, bool fSaveAllProperties)
        {
            WritePropertyBag(pb, "BatchSize", this.BatchSize);
            WritePropertyBag(pb, "BatchRootElementXPath", this.BatchRootElementXPath);
        }

        #endregion

        #region IComponentUI

        /// <summary>
        /// Component icon to use in BizTalk Editor
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public IntPtr Icon
        {
            get
            {
                return ((System.Drawing.Bitmap)(this.resourceManager.GetObject("COMPONENTICON", System.Globalization.CultureInfo.InvariantCulture))).GetHicon();
            }
        }

        /// <summary>
        /// The Validate method is called by the BizTalk Editor during the build of a BizTalk project.
        /// </summary>
        /// <param name="projectSystem"></param>
        /// <returns></returns>
        public System.Collections.IEnumerator Validate(object projectSystem)
        {
            return null;
        }

        #endregion

        #region IDisassemblerComponent

        /// <summary>
        /// Disassembles the specified p context.
        /// </summary>
        /// <param name="pContext">The p context.</param>
        /// <param name="pInMsg">The pInMSG.</param>
        /// <exception cref="System.ApplicationException">
        /// Error in reading original message:  + ex.Message
        /// or
        /// Error in writing outgoing messages:  + ex.Message
        /// </exception>
        public void Disassemble(IPipelineContext pContext, Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
        {
            XPathNavigator xPathNavigator = null;
            XmlNamespaceManager namespaceManager = null;
            XPathNodeIterator nodIterator = null;
            List<string> operationNames = null;

            try
            {
                //fetch original message
                Stream originalMessageStream = pInMsg.BodyPart.GetOriginalDataStream();
                XPathDocument document = new XPathDocument(originalMessageStream);
                xPathNavigator = document.CreateNavigator();
                namespaceManager = GetNameSpacesFromMessage(document);
                nodIterator = xPathNavigator.Select(string.Concat(BatchRootElementXPath, "/*"), namespaceManager);
                operationNames = nodIterator.Cast<XPathNavigator>().Select(node => node.LocalName).Distinct().ToList();
            }

            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in reading original message: ", ex.Message));
                throw new ApplicationException(string.Concat("Error in reading original message: ", ex.Message));
            }


            XmlDocument chunkDoc = null;
            XmlNode batchRootElement = null;
            try
            {
                //load original message
                chunkDoc = new XmlDocument();
                chunkDoc.LoadXml(xPathNavigator.OuterXml);
                batchRootElement = chunkDoc.SelectSingleNode(BatchRootElementXPath, namespaceManager);
                batchRootElement.InnerXml = string.Empty;   //Clear children.

                //fetch namespace and root element
                string namespaceURI = chunkDoc.DocumentElement.NamespaceURI;
                string rootElement = chunkDoc.DocumentElement.LocalName;

                //start batching messages
                int counter = 0;

                foreach(string operationName in operationNames)
                {
                    nodIterator = xPathNavigator.Select(string.Concat(BatchRootElementXPath, string.Format("/*[starts-with(local-name(), '{0}')]", operationName)), namespaceManager);                  

                    while (nodIterator.MoveNext())
                    {
                        counter = counter + 1;
                        if (counter > BatchSize)
                        {
                            CreateOutgoingMessage(pContext, chunkDoc.OuterXml, namespaceURI, rootElement, pInMsg);
                            counter = 1;
                            chunkDoc.LoadXml(xPathNavigator.OuterXml);
                            batchRootElement = chunkDoc.SelectSingleNode(BatchRootElementXPath, namespaceManager);
                            batchRootElement.InnerXml = nodIterator.Current.OuterXml;   //Clear children.
                        }

                        else
                        {
                            batchRootElement.InnerXml = string.Concat(batchRootElement.InnerXml, nodIterator.Current.OuterXml);
                        }
                    }
                    if (!string.IsNullOrEmpty(batchRootElement.InnerXml))   //Splitting last message in the batch
                    {
                        CreateOutgoingMessage(pContext, chunkDoc.OuterXml, namespaceURI, rootElement, pInMsg);                      
                    }

                    batchRootElement.InnerXml = string.Empty;
                    counter = 0;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in writing outgoing messages: ", ex.Message));
                throw new ApplicationException("Error in writing outgoing messages: " + ex.Message);
            }
        }

        /// <summary>
        /// Gets the next message.
        /// </summary>
        /// <param name="pContext">The pContext.</param>
        /// <returns></returns>
        public Microsoft.BizTalk.Message.Interop.IBaseMessage GetNext(IPipelineContext pContext)
        {
            IBaseMessage returnMessage = null;

            if (qOutputMsgs.Count > 0)
            {
                returnMessage =  (IBaseMessage)qOutputMsgs.Dequeue();
            }

            return returnMessage;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Gets the name spaces from message.
        /// </summary>
        /// <param name="doc">The document.</param>
        /// <returns></returns>
        private XmlNamespaceManager GetNameSpacesFromMessage(XPathDocument xDocument)
        {
            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(new NameTable());
            XPathNavigator nav = xDocument.CreateNavigator();
            XPathNodeIterator nodes = (XPathNodeIterator)nav.Evaluate("//namespace::*");

            while (nodes.MoveNext())
            {
                namespaceManager.AddNamespace(nodes.Current.Name, nodes.Current.Value);
            }


            return namespaceManager;
        }

        /// <summary>
        /// Reads the property bag.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyBag">The property bag.</param>
        /// <param name="propName">Name of the property.</param>
        /// <returns></returns>
        private T ReadPropertyBag<T>(IPropertyBag propertyBag, string propName)
        {
            T returnValue = default(T);
            object val = null;
            try
            {
                propertyBag.Read(propName, out val, 0);
                returnValue = (T)Convert.ChangeType(val, typeof(T));
            }
            catch (System.ArgumentException)
            {
                return returnValue;
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in reading into property bag", ex.Message));
                throw new ApplicationException(string.Concat("Error in reading into property bag: ", ex.Message));
            }
            return returnValue;
        }

        /// <summary>
        /// Writes the property bag.
        /// </summary>
        /// <param name="propertyBag">The property bag.</param>
        /// <param name="propName">Name of the property.</param>
        /// <param name="val">The value.</param>
        private void WritePropertyBag(IPropertyBag propertyBag, string propName, object val)
        {
            try
            {
                propertyBag.Write(propName, ref val);
            }
            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("Error in writing into property bag", ex.Message);
                throw new ApplicationException(string.Concat("Error in writing into property bag: ", ex.Message));
            }
        }

        /// <summary>
        /// Queue outgoing messages
        /// </summary>
        private void CreateOutgoingMessage(IPipelineContext pContext, String messageString, string namespaceURI, string rootElement, IBaseMessage pInMsg)
        {

            IBaseMessage outMsg =null;
            string systemPropertiesNamespace = @"http://schemas.microsoft.com/BizTalk/2003/system-properties";
            try
            {
                //create outgoing message
                outMsg = pContext.GetMessageFactory().CreateMessage();
                outMsg.AddPart("Body", pContext.GetMessageFactory().CreateMessagePart(), true);
                outMsg.Context = PipelineUtil.CloneMessageContext(pInMsg.Context);
                outMsg.Context.Promote("MessageType", systemPropertiesNamespace, string.Concat(namespaceURI, "#", rootElement));
                byte[] bufferOoutgoingMessage = System.Text.ASCIIEncoding.ASCII.GetBytes(messageString);
                outMsg.BodyPart.Data = new MemoryStream(bufferOoutgoingMessage);
                qOutputMsgs.Enqueue(outMsg);

            }
            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in queening outgoing messages:", ex.Message));
                throw new ApplicationException(string.Concat("Error in queening outgoing messages: ", ex.Message));
            }
        }

        #endregion
    }


5. Sign and then build project.
6. Copy MessageBatchPipelineCompoent.dll to C:\Program Files\Microsoft BizTalk Server 2006\Pipeline Components.
7. Pipeline component is now ready to use. In BTS pipeline project, add this pipeline component dll in toolbar and then use in disassemble stage.

Usage Scenario:
Sometimes in BizTalk orchestrations/messaging, incoming messages come with huge chunks of records where as a part of business process each record is processed individually one by one and the repeating record is not consistent. Publishing this huge record set in message box and further their processing consumes a lot of resources because orchestration takes a longer time to process each record individually.

Using this custom pipeline, huge messages can be broken into multiple messages of smaller records as per the each record type. And when published in a message box, orchestrations can run in parallel to process more than one message (broken ones) at the same time. Result is better performance and small processing time.

Example:
<root>
<items>
<childItem1>childItem1-1</childItem1>
<childItem1>childItem1-2</childItem1>
<childItem1>childItem1-3</childItem1>
<childItem2>childItem2-1</childItem2>
<childItem2>childItem2-1</childItem2>
<childItem3>childItem3-1</childItem3>
<childItem3>childItem3-2</childItem3>
<childItem3>childItem3-3</childItem3>
<childItem3>childItem3-4</childItem3>
<childItem3>childItem3-5</childItem3>
</items>
</root>

If we specify the BatchSize poperty as 2 and BatchRootElementXPath as //root/items. The output will come as below specified:

For childItem1 2 chunks
For childItem2 1 chunk
For childItem3 3 chunk

Wednesday, January 22, 2014

Programmatically Enable/Disable Receive pipeline


We can achieve this functionality using the  "Microsoft.BizTalk.ExplorerOM" component.

Add reference "Microsoft.BizTalk.ExplorerOM" component to your application.

BtsCatalogExplorer bceExplorer = new BtsCatalogExplorer();
//Edit the following connection string to point to the correct database and server
bceExplorer.ConnectionString = "Integrated Security=SSPI;database=BizTalkMgmtDb;server=localhost";

public bool SetReceiveLocationState(string receivePortName, bool stateValue)
{
            bool returnValue = false;
            try
            {
                ReceivePort receivePort = bceExplorer.ReceivePorts[receivePortName];
                receivePort.ReceiveLocations.Cast<ReceiveLocation>().ToList().ForEach(receiveLocation => receiveLocation.Enable = stateValue);

                bceExplorer.SaveChanges();
                returnValue = true;
            }
            catch (Exception e)
            {
                System.Diagnostics.EventLog.WriteEntry("SetReceiveLocationState", e.Message);
                bceExplorer.DiscardChanges();
                returnValue = false;
            }
            return returnValue;
        }

Programmatically (Dynamically) change the properties of a Receive Location


We can update the properties of a File adapter Receive Location using the Microsoft.BizTalk.ExplorerOM (from {BizTalkInstallation}\DeveloperTools) and I refereed useful article from MSDN for my development.

   We have a requirement to programmatically(dynamically) change the File Mask property of the File receive location.
   The following code only deals with a File Mask property that can be changed in the File receive location.
It accepts few things as input, we accept the port name, which receive location you want to change, file mask and it will change the file mask property in the receive location.


Add reference "Microsoft.BizTalk.ExplorerOM" component to your application.

I have written below as per the MSDN article and this trick didn't worked for me.

BtsCatalogExplorer bceExplorer = new BtsCatalogExplorer();
//Edit the following connection string to point to the correct database and server
bceExplorer.ConnectionString = "Integrated Security=SSPI;database=BizTalkMgmtDb;server=localhost";

public bool UpdateReceiveLocationFileMask(string receivePortName, string receiveLocationName, string fileMask)
{
            bool returnValue = false;
            string transportData = string.Empty;
            XmlDocument doc = null;
            XmlNode root = null;
            XmlNode fileMaskNode = null;
            try
            {
                if (!string.IsNullOrEmpty(fileMask))
                {
                    ReceivePort receivePort = bceExplorer.ReceivePorts[receivePortName];
                    ReceiveLocation receiveLocation = receivePort.ReceiveLocations.Cast<ReceiveLocation>().Where(item => item.Name == receiveLocationName).FirstOrDefault();

                    if (receiveLocation != null)
                    {                        
                        transportData = receiveLocation.TransportTypeData;
                        doc = new XmlDocument();
                        doc.LoadXml(transportData);
                        root = doc.DocumentElement;
                        fileMaskNode = root.SelectSingleNode("FileMask");

                        if (fileMaskNode != null)
                        {
                            fileMaskNode.InnerText = fileMask;
                            receiveLocation.TransportTypeData = doc.OuterXml;
                            bceExplorer.SaveChanges();
                            returnValue = true;
                        }
                        bceExplorer.SaveChanges();
                        returnValue = true;
                    }
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.EventLog.WriteEntry("UpdateReceiveLocationFileMask", e.Message);
                bceExplorer.DiscardChanges();
                returnValue = false;
            }

            return returnValue;


The above code didn't worked, i have used Address property of the receive location and this trick worked for me.

public bool UpdateReceiveLocationFileMask(string receivePortName, string receiveLocationName, string fileMask)
{
            bool returnValue = false;
            string transportData = string.Empty;
            XmlDocument doc = null;
            XmlNode root = null;
            XmlNode fileMaskNode = null;
            try
            {
                if (!string.IsNullOrEmpty(fileMask))
                {
                    ReceivePort receivePort = bceExplorer.ReceivePorts[receivePortName];
                    ReceiveLocation receiveLocation = receivePort.ReceiveLocations.Cast<ReceiveLocation>().Where(item => item.Name == receiveLocationName).FirstOrDefault();

                    if (receiveLocation != null)
                    {
                        receiveLocation.Address = Path.Combine(Path.GetDirectoryName(receiveLocation.Address), fileMask);                        
                        bceExplorer.SaveChanges();
                        returnValue = true;
                    }
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.EventLog.WriteEntry("UpdateReceiveLocationFileMask", e.Message);
                bceExplorer.DiscardChanges();
                returnValue = false;
            }

            return returnValue;
}