This blog post is about creating a custom BizTalk pipeline that allows the original data stream to be seekable. If the position of this stream is not reset when it’s returned then the message will be empty as it is by default forward-only.
A seekable stream in a pipeline
I won’t go into details of how the pipeline is structured, which interfaces to implement, etc. You can find the full documentation on how to create a custom BizTalk pipeline at this MSDN article.
Let’s first have a look at the most important method of the pipeline: Execute. This is the main method of the pipeline which is called with context and the actual message being processed is passed to it as parameters.
1: public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
2: {
3: // Create a seekable message from the IBaseMessage passed to the pipeline
4: IBaseMessage seekableMessage = CreateSeekableMessage(pInMsg);
5:
6: try
7: {
8: DoSomethingWithMessage(seekableMessage);
9: }
10: catch (Exception ex)
11: {
12: // If we cannot do something with the message then write to the event log that the pipeline failed
13: EventLog.WriteEntry("DBLOG.BizTalk.PipelineSample", ex.Message, EventLogEntryType.Error);
14: }
15: finally
16: {
17: // Always reset the message stream
18: ResetMessageStream(seekableMessage);
19: }
20:
21: // Pass the message back regardless if we managed to do something with it or not
22: return seekableMessage;
23: }
From the code sample above we have two interesting things happening:
- A method called CreateSeekableMessage is called which takes the type IBaseMessage as parameter
- A method called ResetMessageStream is finally called before the message is returned
Let’s have a closer look at the CreateSeekableMessage-method and see exactly what it does.
1: private IBaseMessage CreateSeekableMessage(IBaseMessage message)
2: {
3: Stream originalDataStream = message.BodyPart.GetOriginalDataStream();
4:
5: // Only create the new seekable stream if the original data stream does not have seek capability
6: if (!originalDataStream.CanSeek)
7: {
8: ReadOnlySeekableStream seekableDataStream = new ReadOnlySeekableStream(originalDataStream);
9: message.BodyPart.Data = seekableDataStream;
10: }
11:
12: return message;
13: }
In this method a new object of type ReadOnlySeekableStream is created from the original data stream. From here on the stream will be seekable which means we can read from it and ultimately reset the position and pass it back and into BizTalk’s message box!
So without pause let’s dive into the ResetMessageStream-method!
1: private void ResetMessageStream(IBaseMessage message)
2: {
3: // Reset the stream to position 0
4: message.BodyPart.Data.Seek(0, SeekOrigin.Begin);
5: }
This method does just what it says: resets the stream of the message-object to position 0!
Final words
When creating pipelines try to keep the complexity to a minimum. I have seen pipelines where all logic has been stuffed into the Execute-method. While the pipeline might do its job the next developer having a look at your code is going to have a bad day.
Try to make your code as readable as possible!