Scenario
You would like to set the name of a destination file dynamically using a Send Pipeline. Moreover, you would like to set such name based on a message (field) element value.
Assumption
It’s possible to promote a schema field element to the ReceivedFileName property of global property schema FILE.bts_file_properties. However, in this scenario we’re assuming that such promoted value would not be available to the Send Pipeline due to additional processing steps that would reset the ReceivedFileName context property.
Solution
Create a custom pipeline component to set the ReceivedFileName message context property based on a configurable XPath Instance (node path). The following are code snippets of such pipeline component:
Note: Refer to http://msdn.microsoft.com/en-us/library/ee377071%28BTS.10%29.aspx for optimizing xml processing inside a pipeline.
[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[ComponentCategory(CategoryTypes.CATID_Encoder)]
public class SourceFileName : IBaseComponent, Microsoft.BizTalk.Component.Interop.IComponent, IComponentUI, IPersistPropertyBag
{
#region IComponent Members
public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
try
{
string xPath = InstanceXPath;
string propertyValue = string.Empty;
int bufferSize = 0x280;
int thresholdSize = 0x100000;
IBaseMessageContext messageContext = pInMsg.Context;
if (string.IsNullOrEmpty(xPath))
{
throw new ArgumentException("XPath Instance is missing");
}
IBaseMessagePart bodyPart = pInMsg.BodyPart;
Stream inboundStream = bodyPart.GetOriginalDataStream();
VirtualStream virtualStream = new VirtualStream(bufferSize, thresholdSize);
ReadOnlySeekableStream readOnlySeekableStream = new ReadOnlySeekableStream(inboundStream, virtualStream, bufferSize);
XmlTextReader xmlTextReader = new XmlTextReader(readOnlySeekableStream);
XPathCollection xPathCollection = new XPathCollection();
XPathReader xPathReader = new XPathReader(xmlTextReader, xPathCollection);
xPathCollection.Add(xPath);
bool ok = false;
while (xPathReader.ReadUntilMatch())
{
if (xPathReader.Match(0) && !ok)
{
propertyValue = xPathReader.ReadString();
pInMsg.Context.Write("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties", propertyValue);
ok = true;
}
}
readOnlySeekableStream.Position = 0;
bodyPart.Data = readOnlySeekableStream;
}
catch (Exception ex)
{
if (pInMsg != null)
{
pInMsg.SetErrorInfo(ex);
}
throw ex;
}
return pInMsg;
}
#endregion
#region IPersistPropertyBag Members
private string _instanceXPath;
[
DisplayName("%SourceFileName% XPath Instance"),
Description("XPath Instance for %SourceFileName% macro")
]
public string InstanceXPath
{
get { return _instanceXPath; }
set { _instanceXPath = value; }
}
public void Load(IPropertyBag propertyBag, int errorLog)
{
string text;
text = (string)ReadPropertyBag(propertyBag, "InstanceXPath");
if (text != null) _instanceXPath = text;
}
public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
{
object val;
val = (object)_instanceXPath;
WritePropertyBag(propertyBag, "InstanceXPath", val);
}
#endregion
This sample pipeline component is designed to be used in the Encode stage of a pipeline. Configure the pipeline with the desired node path (e.g. /*[local-name()='Input' and namespace-uri()='']/*[local-name()='MyElement' and namespace-uri()='']) and then specify the SourceFileName macro as part of the destination file name (e.g. %SourceFileName%_%MessageID%.xml).