Maper Pipeline Component

Introduction to Mapping

In a traditional BizTalk project, we use the out of the box BizTalk mapper to
map source messages to destination messages. The Biztalk mapper provides us a
graphical interface for this mapping task. But sometimes, we hardly need only
XSLT to transform the messages. So, we create a map with the source and
destination messages. But the actual mapping will be taken from the XSLT file
that is included with the project. In that case creating a map is long step in
attaining a simple transform process. This gave me an idea of developing the
tranform concept in a pipeline component, wherein you don’t have to actually
create a map for transforming the message.

Audience

  • Nothing more complex is dealt here. A fair understanding of BizTalk pipeline
    component development
  • A good knowledge in XML, XPath, XSLT programming in C#

A background of the pipeline component

I have made this component as simple as possible. If you can look into the
code for developing a simple decoder pipeline component and then walk through my
code, you will understand it better. There are many articles available for
developing a
simple decoder pipeline component. So, I am not going in deeper
to explain to you the basics of component development.

Actual Implementation

For the custom component development, as we all know by this time, we need to
implement some interfaces. So for developing a decoder component we need to
implement IComponent, IBaseComponent,
IPersistPropertyBag and IComponentUI interfaces. In
our scenario, we need to show a custom dialog box for feeding in the XSLT. This
dialog box should be able to be triggerred from the Properties Window for the
custom pipeline component in the custom receive pipeline. For implementing it,
we need to design a class that derives from UITypeEditor. The
property grid you see in the pipeline editor is a .NET property grid. By
default, the grid can only understand string values.

To notify the
property grid that you need a dialog box rather than the default text editor,
you must perform the following steps.

Implement the UITypEditor class.

[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = “FullTrust”)]
class CustomXSLTUITypeEditor : UITypeEditor
{

}

Override the GetEditStyle method to return a Modal
dialog box.


public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
// Indicates that this editor can display a Form-based interface.

return UITypeEditorEditStyle.Modal;
}

Here we override the EditValue method and provide the  implementation for showing up a custom dialog box.

public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
// Attempts to obtain an IWindowsFormsEditorService.

IWindowsFormsEditorService edSvc =
(IWindowsFormsEditorService)provider.GetService
(typeof(IWindowsFormsEditorService));
if (edSvc == null)
{
return null;
}

// Displays a StringInputDialog Form to get a user-adjustable
string value.

using (CustomXSLTPanel form = new CustomXSLTPanel((string)value))
{
XmlDocument xdoc = new XmlDocument();
if (edSvc.ShowDialog(form) ==
System.Windows.Forms.DialogResult.OK)
{
try
{
xdoc.LoadXml(form.txtXSLT.Text);
return form.txtXSLT.Text.Replace(“\n”,”>”);
}
catch (XmlException ex)
{
System.Windows.Forms.MessageBox.Show(“The XSLT is invalid. Please try again”,”Error”, System.Windows.Forms.MessageBoxButtons.OK,System.Windows.Forms.MessageBoxIcon.Error);
return value;
}
}
}

// If OK was not pressed, return the original value

return value;
}

The CustomXSLTPanel class is a custom Windows Form that contains
a RichTextBox control that can

be used to get the XSLT
input from the user. This is a very simple Form.

Having provided all the basic things needed for our Custom component, we go
deep into implementing it. The first thing in

proceeding to our
component is to implement the basic interfaces. The code looks like this.

[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[ComponentCategory(CategoryTypes.CATID_Decoder)]
[System.Runtime.InteropServices.Guid("
9d0e4103-4cce-4536-83fa-4a5040674ad6")]
public class MapperGUI :  IBaseComponent,
                          IComponentUI,
                          Microsoft.BizTalk.Component.Interop.IComponent,
                          IPersistPropertyBag
{

}

We need to implement the IBaseComponent members for the basic
information of our component.

#region IBaseComponent Members

public string Description
{
get
{
return “Pipeline component used as a BizTalk Mapper”;
}
}
public string Name
{
get
{
return “MapperComponent”;
}
}
public string Version
{
get
{
return “1.0.0.0”;
}
}

#endregion

I have made it simple, as I have not created any icon nor added any
validation logic. Go ahead if you want to play with

it.

#region IComponentUI Members

public IntPtr Icon
{
get
{
return new System.IntPtr();
}
}

public System.Collections.IEnumerator Validate(object projectSystem)
{
return null;
}

#endregion

We are going to have a property for getting the XSLT as a string value.

So, we create a  private variable and a  public property. There is a trick  involved in creating the property. We need to add the EditorAttribute class to the property. The actual purpose  is that we need to invoke a dialog box for populating this property. We  pass our custom UITypeEditor class as a paramter. Also we need to  implement the Load and Save methods. I have  not showed that here. But you can find it in the source code that I have provided with this article.

Here comes the most important part of our component development. We implement  the only method of the Icomponent interface. I have kept  the implementation very simple. This actually involves some XML, XPath and XSLT  coding to transform the input message to the output message. I have used  only memory stream all over the code.

#region IComponent Members

        public IBaseMessage Execute(IPipelineContext pContext,
                            IBaseMessage pInMsg)
        {
            IBaseMessagePart bodyPart = pInMsg.BodyPart;
            Stream xmlData = bodyPart.Data;
            Stream transformedMsg = TransformMessage(xmlData);
            pInMsg.BodyPart.Data = transformedMsg;
            pContext.ResourceTracker.AddResource(transformedMsg);
            return pInMsg;
        }

#endregion

This private method takes in an  input message as stream and processes the message. After  transforming the message, it converts that into a stream and returns back. If you  walk through the code, you would find it very simple.

#region private members

        private Stream TransformMessage(Stream inputMessage)
        {
            XslCompiledTransform transformer = new XslCompiledTransform();

            byte[] outBytes = System.Text.Encoding.ASCII.GetBytes
                            (CustomXSLT);

            MemoryStream memStream = new MemoryStream();
            memStream.Write(outBytes, 0, outBytes.Length);
            memStream.Position = 0;

            XPathDocument xsltDoc = new XPathDocument((Stream)memStream);
            MemoryStream destnStream = new MemoryStream();
            transformer.Load(xsltDoc);

            XPathDocument doc = new XPathDocument(inputMessage);

            transformer.Transform(doc, null, destnStream);
            return (Stream)destnStream;
        }

#endregion

Is there any background to this article that may be useful such as an
introduction to the basic ideas presented?

I found a whitepaper written by Saravanan regarding design time properties in
custom pipeline component. You can find the

document here.

Points of Interest

As this is my very first presentation of an article, I might have missed
something that is more important to explain.

Please bear with me.
Alternatively you can email me at shankar.sekar@gmail.com so that it will help
me to improve the

component development or the presentation. All your
comments are welcome.

I also have to discuss the downside of using this control. As I have designed
this control as a decoder component, we

can’t use this to validate the
XML message. So, we can only use a passthrough send pipeline in the send port. I
have future

plans to extend this control to participate in all stages of
the pipeline. Please provide your valuable comments to develop

further.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s