Storing Context properties in Processing instructions

By patrickwellink
September 25, 2007
Comments Off on Storing Context properties in Processing instructions

If you are working with BizTalk you have probably used context properties for routing etc. Sometimes you want to inspect a message and see what the context properties are.
Or if you have build a (kind of) ESB using the messagebox it would be nice to have a listener and spit all the messages that hit the ESB to a specific directory just for diagnostic purposes. But you want to be able to see the context properties together with the message.


I have build a very simple pipeline component to do just that. Below is the listing of the pipeline component that writes a context property to a processing instruction :



  public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(Microsoft.BizTalk.Component.Interop.IPipelineContext pc, Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg)
  {
   MemoryStream  memStream;
   Stream originalStrm;
   originalStrm = null;
   memStream = new MemoryStream();
   // System.Diagnostics.Debugger.Launch();


   IBaseMessagePart bodyPart = inmsg.BodyPart;


   if (bodyPart!=null)
   {
    originalStrm = bodyPart.GetOriginalDataStream();


    if (originalStrm != null)
    {
     originalStrm.Seek(0,System.IO.SeekOrigin.Begin);
     // Create a new XML doc
     XmlDocument xDoc = new XmlDocument();
     xDoc.Load(originalStrm);


     // Now read the Context property
     IBaseMessageContext context = inmsg.Context;
     object obj = context.Read( ContextPropertyName , ContextPropertyNameSpace);
     if (null != obj)
     {
      // do some work with each value that was promoted
      string ProcessingInstruction = (string)obj;
      // create a new processing instruction
      XmlProcessingInstruction piNew = xDoc.CreateProcessingInstruction(ContextPropertyName, ProcessingInstruction ); 


      // Insert before the first node of the root
      xDoc.DocumentElement.InsertBefore(piNew,xDoc.DocumentElement.FirstChild  );
     }
     //Write XML to a memory stream
     xDoc.Save(memStream);
     memStream.Seek(0,SeekOrigin.Begin);


     //Change the data
     bodyPart.Data = memStream;


     //The memory stream object must persist after you’ve
     //left the function
     pc.ResourceTracker.AddResource( memStream );
    }
   }
   return inmsg;    
  }


And here is the code that writes a processing instruction to a context property. After adding a PI to the context we have to make sure we remove it from the original underlying XML otherwise if a message would be send again using these pipelines we would double the processing instructions.



  public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(Microsoft.BizTalk.Component.Interop.IPipelineContext pc, Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg)
  {
   MemoryStream  memStream;
   Stream originalStrm;
   originalStrm = null;
   memStream = new MemoryStream();
   string PropertyValue = string.Empty ;
    


   IBaseMessagePart bodyPart = inmsg.BodyPart;


   if (bodyPart!=null)
   {
    originalStrm = bodyPart.GetOriginalDataStream();


    if (originalStrm != null)
    {
     originalStrm.Seek(0,System.IO.SeekOrigin.Begin);
     // Create a new XML doc
     XmlDocument xDoc = new XmlDocument();
     xDoc.Load(originalStrm);


     // Now get the desired processing instruction
     string PropertyName = “”” + ContextPropertyName  + “””;
     string SelectString = “//processing-instruction(” + PropertyName + “)”;
     XmlNode contextpropertyNode = xDoc.SelectSingleNode(SelectString);
     if (null != contextpropertyNode)
     {
      PropertyValue = (string)contextpropertyNode.Value;
      // Now remove the processing instruction
      xDoc.DocumentElement.RemoveChild(contextpropertyNode);
     }
     
     //Write XML to a memory stream
     xDoc.Save(memStream);
     memStream.Seek(0,SeekOrigin.Begin);


     //Change the data
     bodyPart.Data = memStream;


     //The memory stream object must persist after you’ve
     //left the function
     pc.ResourceTracker.AddResource( memStream );
    }
   }
   if (PropertyValue != string.Empty)
   {
    inmsg.Context.Promote(ContextPropertyName , ContextPropertyNameSpace , PropertyValue );
   }
   return inmsg;    
  }


Below are examples of how to configure them. In our solution we have several ESB properties so you just have to add multiple pipeline components. Below is the receive pipeline. This is PartyResolution pipeline component, so we already know it’s a valid known schema to BizTalk.


And inject context properties as processing instructions in a XML document. Again we have multiple Context properties so we have to add multiple pipeline components.



It’s all quite basic and probably using a XMLdocument isn’t the fastest solution. However, it still is very fast and works very well. Below is a sample xml that is written to a Listen directory.



The fun part is, with the Receive pipeline you can drop files with processing instructions and have the processing instructions written to the context of the received message so they can continue their execution inside the ESB.


There are probably more ways of doing this but for now this seems a nice solution that I wanted to share on my blog.

Comments: 0

Comments are closed.

  • Recent Posts
  • Recent Comments
  • Archives
  • Categories
  • Meta