A lesson I learnt (the hard way) while working on the BRE Pipeline Framework was that if you use one of the out of box disassembler pipeline components such as the XML/EDI/Flat File disassembler and you rely on them to promote context properties from the body of your message, you will find that those context properties are not promoted until the message stream has been read at least once.
What this actually means is that if you build a custom pipeline component which you intend to use in your receive pipeline after a disassembler, and you try to read a context property that you expected to be promoted by the disassembler, you will find that it has a null value if you don’t read the stream.
However, if you perform a read of the inbound stream using a StreamReader (remember to add the reader as a resource to the pipeline context like below so that it won’t get disposed till the pipeline completes processing, to ensure that your stream is seekable and to rewind the stream after reading it) you will find that all your context properties are promoted.
StreamReader reader = new StreamReader(inmsg.BodyPart.Data); reader.Read(); pc.ResourceTracker.AddResource(reader); copiedBodyPart.Data.Position = 0;
One other interesting fact is that even if you call the StreamReader.Read() method which should only read one character from the stream, you will find that all your context properties are promoted, and in fact the current position in the stream (before you reset it of course) is the end of the stream!
So why does this behaviour happen? It turns out that when the out of the box disassemblers execute they don’t actually promote the context properties themselves. They actually wrap the inbound stream with a Microsoft.BizTalk.Component.XmlDasmStreamWrapper wrapper stream, which is responsible for promoting properties amongst other tasks. The property promotion does not happen until the stream has been read. This also hold true for other context processing type functions that are performed by disassemblers, as detailed so well by Charles Young in this blog post about BizTalk Server 2004 (from which I’ve borrowed part of this blog’s title).
This behaviour is in line with best practice pipeline component development guidance, which suggests that pipeline components should be built in an efficient stateless manner in which content processing is only executed once when the stream is read as the message is committed to the BizTalk message box (rather than within the pipeline components themselves) with the use of wrapper streams. This model suggests that all content processing should be carried out either when a pipeline completes execution or in orchestrations.
This guidance works well for solutions which include orchestrations which provide workflow functionality, but obviously doesn’t hold water in messaging only solutions (of course this could spurn a debate as to whether a solution that mandates content processing must contain orchestration, however that is not a topic for today) in which you might want to perform some evaluation of the message content (potentially via context properties) in a pipeline. In this case you might have no choice but to read the stream prior to attempting your own processing if you depend on having available the outputs of a preceding disassembler.
You will also find if you are creating a new message in your pipeline and copying over the content and context from the original message to your new message, that if you don’t read the stream and attempt to perform a shallow copy of the context from the original message to the target message, that your target message will lose any context properties that the disassembler was meant to promote. You should instead copy the context over by reference.
So how this affect the BRE Pipeline Framework? Prior to v1.5 I was naively making a copy of the original stream from the source message to the target output message, which of course means that the stream was being read. This is certainly not the most efficient way to create the output message, especially if I don’t intend to manipulate the message body in any way since passing the original stream by reference should be good enough. I decided that this was the path I would go down with the BRE Pipeline Framework v1.5. However, of course, without reading the stream the context properties would no longer be available for evaluation in rules that are executed by the BRE Pipeline Framework, as was the case with prior versions of the framework.
To get around this issue the pipeline component included in the BRE Pipeline Framework has a new parameter called StreamsToReadBeforeExecution, which is a comma separated list of stream types that should be read prior to calling any BRE Policies, and it is pre-populated with the value Microsoft.BizTalk.Component.XmlDasmStreamWrapper. If you are building a solution based on the BRE Pipeline Framework and do not need access to context properties that are promoted by disassemblers then I would urge you to remove the value from this parameter on the pipeline component so that your pipeline component behaves in a streaming manner. Rest assured that regardless whether the parameter is populated or not, the promoted context properties will be on the output message once the pipeline has completed processing.
You will find that if you run a trace using the CAT Instrumentation Framework Controller, specifically for pipeline component tracing, that the stream type intercepted by the BRE Pipeline Framework component will be displayed and if the stream is being read that will be displayed as well as below.
135C.09E0::07/10/2014-22:10:29.877 [Event]:9d59b3fb-5a39-43a3-8b90-7d33a5b2ec17 - Inbound message body had a stream type of Microsoft.BizTalk.Component.XmlDasmStreamWrapper 135C.09E0::07/10/2014-22:10:29.877 [Event]:9d59b3fb-5a39-43a3-8b90-7d33a5b2ec17 - Inbound message body stream was not seekable so wrapping it with a ReadOnlySeekableStream 135C.09E0::07/10/2014-22:10:29.890 [Event]:9d59b3fb-5a39-43a3-8b90-7d33a5b2ec17 - Reading stream to ensure it's read logic get's executed prior to pipeline component execution
The takeaway from this blog post is that you must not assume that context properties will be available for evaluation in your pipeline components following a disassembler unless you read the stream first (which might or might not be acceptable, based on your specific requirements).