The BizTalk Sever SDK comes with an interesting Pipeline component sample that allows executing Xsl mapping files in a pipeline.
This functionality can be of interest when the receive port or send port mappings are not executed at the desired time! Receive port mappings are executed after the pipeline execution, send port mappings are executed before the pipeline execution. Thanks to this pipeline component you get control over the exact time when a transformation gets executed.
Although this functionality can be very useful the code of this sample contains some problems that need fixing before using this in production environments.
Let’s take a look at the business end of the sample, the TransformMessage method (you can find the original sample code in this location: <BizTalk installation directory>\SDK\Samples\Pipelines\XslTransformComponent):
private Stream TransformMessage(Stream stm)
{
MemoryStream ms = null;
string validXsltPath = null;
try
{
// Get the full path to the Xslt file
validXsltPath = GetValidXsltPath(xsltPath);
// Load transform
XslTransform transform = new XslTransform();
transform.Load(validXsltPath);
//Load Xml stream in XmlDocument.
XmlDocument doc = new XmlDocument();
doc.Load(stm);
//Create memory stream to hold transformed data.
ms = new MemoryStream();
//Preform transform
transform.Transform(doc, null, ms, null);
ms.Seek(0, SeekOrigin.Begin);
}
catch(Exception e)
{
System.Diagnostics.Trace.WriteLine(e.Message);
System.Diagnostics.Trace.WriteLine(e.StackTrace);
throw e;
}
return ms;
}
The signature of the method accepts a Stream as input parameter and returns a Stream as result. Perfect to keep everything streaming…
Then a XslTransform object is created. At first, this might look like a good idea, but XslTransform will load the message into memory internally! For smaller messages this will not cause any issues but bigger messages will cause a System.OutOfMemoryException…
To get rid of this problem I replaced the XslTransform class with the BTSXslTransform class. This class uses the BizTalk transformation engine. The BizTalk transformation engine will only load small messages into memory and will use disk space if the message size reaches a certain threshold.
The following line instantiates the BizTalk mapper engine (add a reference to Microsoft.XLANGs.BaseType)
BTSXslTransform trans = new BTSXslTransform();
Use the following line to execute the transform:
trans.ScalableTransform(inputReader, null, vs, new XmlUrlResolver(), false)
The following problem we encounter in the original sample is this section:
XmlDocument doc = new XmlDocument();
doc.Load(stm);
This section reads the stream into memory again via an XmlDocument.
I replace this section with this line:
XmlTextReader inputReader = new XmlTextReader(stm);
Instead of feeding the message to the transform method as one memory chunk, I present it a stream that can be pulled by the BTSXslTransform object.
There is one last thing to change in this sample to make it suitable for large messages:
ms = new MemoryStream();
A MemoryStream is used to store the result of the transform operation. Unfortunately a MemoryStream uses memory to store the stream. This makes it unusable for large messages.
Luckily BizTalk Server comes with a stream class that has the same functionality as MemoryStream but one that uses disk space to store large streams. This class is the VirtualStream class (add a reference to Microsoft.BizTalk.Streaming).
Instantiate a VirtualStream object to hold the result of the transformation:
vs = new VirtualStream(VirtualStream.MemoryFlag.AutoOverFlowToDisk);
The AutoOverFlowToDisk instructs this stream to use disk space for large messages.
The changed TransformMessage function now looks like this:
private Stream TransformMessage(Stream stm)
{
VirtualStream vs = null;
string validXsltPath = null;
try
{
// Get the full path to the Xslt file
validXsltPath = GetValidXsltPath(xsltPath);
XmlTextReader stylesheet = new XmlTextReader(validXsltPath);
// Load transform
BTSXslTransform trans = new BTSXslTransform();
trans.Load(stylesheet);
XmlTextReader inputReader = new XmlTextReader(stm);
//Create memory stream to hold transformed data.
vs = new VirtualStream(VirtualStream.MemoryFlag.AutoOverFlowToDisk);
//Preform transform
trans.ScalableTransform(inputReader, null, vs, new XmlUrlResolver(), false);
vs.Seek(0, SeekOrigin.Begin);
}
catch(Exception e)
{
System.Diagnostics.Trace.WriteLine(e.Message);
System.Diagnostics.Trace.WriteLine(e.StackTrace);
throw e;
}
return vs;
}
When this code is compiled into a pipeline component, it allows executing large transformation exactly where you want it in the BizTalk pipeline infrastructure. This component will keep memory consumption flat, even when processing very large Xml files.
The source code of this article can be downloaded from this post as attachement.
XslTransform.zip (30,62 kb)
Peter Borremans
a3c6a2bc-d7a2-447d-91d8-3d743f385fd3|6|5.0