It is critical to understand that each of the
different types of pipeline components is designed to do a specific
task. Most custom pipelines that we have seen generally include only one
custom pipeline component inside a custom pipeline and try to perform
all tasks within that one class. Often this becomes problematic and
leads to an overly complex class. When starting development, the goal
should be to write many small generic components instead of a small
number of specialized ones. Pipeline components should be designed to be
reused. Additionally, if it makes sense, the components should also
accept multiple schema types and allow the user to choose the
inbound/outbound schemas. Too often developers will hard-code the schema
into the component and assume that the inbound messages will always be
of a certain type. The component should, first, be flexible enough to
probe the incoming schema and determine that it can handle the document
and should, second, provide a user interface for selecting the desired
schema in the Pipeline Designer.
Before starting a new
project, the most important thing is to understand what type of
component you need to build. This will depend on the following:
Whether the component is executed in a receive pipeline, a send pipeline, or both
What type of work needs to be done
Too often developers simply
"default" to writing what they know instead of what they should. Having a
thorough understanding of how pipeline components work, along with a
proper design for what work should take place at which pipeline stage,
will help you build more reusable and reliable pipeline components.
1. Component Categories
As you will soon see, the
pipeline component development API is quite heavily based on COM
Interop. Most of the specifics of how components are defined are based
on what COM category they are tagged with along with what interfaces
they implement. Component categories, listed here, exist for each of the
different types of pipeline components:
CATID_Any
CATID_AssemblingSerializer
CATID_Decoder
CATID_DisassemblingParser
CATID_Encoder
CATID_Parser
CATID_PartyResolver
CATID_PipelineComponent
CATID_Receiver
CATID_Serializer
CATID_Streamer
CATID_Transmitter
CATID_Validate
These are defined by tagged attributes on the pipeline component's class, as shown in the following code:
Imports System
Imports System.ComponentModel
Imports Microsoft.BizTalk.Message.Interop
Imports Microsoft.BizTalk.Component.Interop
Imports Microsoft.BizTalk.Component
Imports Microsoft.BizTalk.Messaging
Namespace ABC.BizTalk.FullFillment.PipelineComponents
<ComponentCategory(CategoryTypes.CATID_PipelineComponent), _
System.Runtime.InteropServices.Guid("4f1c7d50-e66f-451b-8e94-2f8d599cd013"), _
ComponentCategory(CategoryTypes.CATID_Encoder)> _
Public Class MyFirstEncodingComponent
Note that it is possible
for a component to have more than one category type if it has the proper
interface implementation. Also note that the component explicitly
defines a GUID for COM Interop.
This can be done by generating a new GUID—using, for example, the VS
.NET GUID Generation tool—and adding it here as an attribute of the
class.
2. Component Interfaces
The specific interfaces a
pipeline component implements are what differentiate that pipeline
component from another. BizTalk Server ships with a number of assemblies
that define application interfaces for custom components to implement.
Once a component has an interface implementation, it can be called by
the BizTalk runtime. The basic interfaces are defined in the following
list. All components and component categories live in the Microsoft.BizTalk.Component.Interop namespace.
IBaseComponent: Defines properties that provide basic information about the component.
Public properties:
Description: Gets the component description
Name: Gets the component name
Version: Gets the component version
IComponent: Defines the methods used by all pipeline components except Assemblers and Disassemblers.
Public method:
IComponentUI: Defines methods that enable pipeline components to be used within the Pipeline Designer environment.
Public property:
Public method:
3. Key BizTalk API Objects
All pipeline components,
regardless of what they do, will use the interfaces described in the
following subsections. Likewise, most of the pipeline component
interfaces defined previously accept these interfaces as arguments. That
being said, it is important to understand these interfaces first before
proceeding. The following is based on material from the BizTalk Server
documentation on MSDN.
1. IPipelineContext
IPipelineContext is the main interface that defines the operations and properties for the pipeline instance. Tables 1 and 2 show the interface's public properties and methods. The key method here is the GetMessageFactory method. This method will return a message factory for the pipeline that can be used to create new messages using the CreateMessage method.
The GetMessageFactory method is the main way to create new messages from within a pipeline component. Also note that you will need to call IPipelineContext. GetMessageFactory.CreateMessagePart to create the message part for the messageA message is simply a construct that contains zero to many message
parts. Once you create the message part, you can assign it to the IBaseMessage object through the AddPart method.
Table 1. IPipelineContext Public Properties
Property | Description |
---|
ComponentIndex | Gets the index of the current component in the stage. |
PipelineID | Gets the ID of the pipeline with which this pipeline context associates. |
PipelineName | Gets the name of the pipeline. |
ResourceTracker | Gets the IResourceTracker object associated with the pipeline context. This object can be used to track and dispose non-CLR resources. |
StageID | Gets the ID of the current stage in the pipeline. |
StageIndex | Gets the index of the pipeline stage where the current component is located. |
Table 2. IPipelineContext Public Methods
Method | Description |
---|
GetDocumentSpecByName | Gets an IDocumentSpec object for the specified document name |
GetDocumentSpecByType | Gets an IDocumentSpec object for the specified document type |
GetGroupSigningCertificate | Gets the signing certificate for the group |
GetMessageFactory | Get access to the helper interface to work with BizTalk Server message objects |
3.2. IBaseMessage
IBaseMessage is the base interface that defines a BizTalk Server message. Tables 3 and 4 show the public properties and methods. Note that messages, created using the IPipelineContext.GetMessageFactory.CreateMessage method, will implement this interface. This will still need an IBaseMessagePart to be assigned through the AddPart method for any data to be included with the message.
Table 3. IBaseMessage Public Properties
Property | Description |
---|
BodyPart | Gets the body part, or main part, of the message |
BodyPartName | Gets the name of the body part, or main part, of the message |
Context | Gets or sets the message context |
IsMutable | Gets a value indicating whether the message can be changed by components during processing |
MessageID | Gets the unique message ID for the message |
PartCount | Gets the total number of parts in the message |
Table 4. IBaseMessage Public Methods
Method | Description |
---|
AddPart | Adds a part to the message. |
GetErrorInfo | Gets the exception that caused the error. |
GetPart | Accesses the message part. This is indexed by PartName. |
GetPartByIndex | Retrieves a part and its name by supplying the part index. |
GetSize | Gets the size of the message. |
RemovePart | Removes a part from the message. |
SetErrorInfo | Sets the error information. |
3.3. IBaseMessagePart
IBaseMessagePart is the interface that defines a BizTalk message part. Tables 5 and 6 show its public properties and methods. The message part is assigned to an IBaseMessage through the AddPart method. Note the Data property. This is the property used to assign a value to the message part. The Data
property accepts and returns only streams. This is incredibly useful,
because it allows any stream to be assigned to the message part. This
includes XMLReader streams, MemoryStreams, and raw BinaryStreams.
Another rarely used item is the PartProperties
property. This is essentially a property bag that can be used to store
information and metadata about the part. In reality, the PartID, Charset, and ContentType are actually contained in the PartProperties IBasePropertyBag object.
Table 5. IBaseMessagePart Public Properties
Property | Description |
---|
Charset | Gets or sets the character set property for the part |
ContentType | Gets or sets the content type property for the part |
Data | Gets or sets the part data |
PartID | Gets the part with a unique ID |
PartProperties | Gets or sets one or more properties that describe the part data or contain custom information about the part |
Table 6. IBaseMessagePart Public Methods
Method | Description |
---|
GetOriginalDataStream | Retrieves the original uncloned version of the part data stream |
GetSize | Retrieves the size of the part |
3.4. IBaseMessageContext
IBaseMessageContext is the interface used to interact with and manipulate the object context accessible through the IBaseMessage.Context property. Tables 7 and 8 show the public properties and methods. The main items of interest here are the Promote, Read, and Write
methods. Properties that exist in the context can never have a Null
value. A Null value means that the property does not exist. Here's an
example:
Table 7. IBaseMessageContext Public Property
Property | Description |
---|
CountProperties | Gets the number of properties in the message context |
Table 8. IBaseMessageContext Public Methods
Method | Description |
---|
AddPredicate | Adds a message predicate to the message |
GetPropertyType | Gets the type of the property |
IsMutable | Indicates whether the message context can be changed by components during processing |
IsPromoted | Enables the component to determine whether a property has already been promoted in the message context |
Promote | Promotes a property into the message context |
Read | Gets a property value from the message context by the name-namespace pair |
ReadAt | Gets a property from the message context by index |
Write | Writes a property into the message context |
3.5. PipelineUtil
PipelineUtil is a helper class that exposes the three methods shown in Table 9. These methods are invaluable when you need to create a new message.
Table 9. PipelineUtil Public Methods
Method | Description |
---|
CloneMessageContext | Creates
a clone of the message context for a given message. This is useful for
copying the message context of one message to another. |
CopyPropertyBag | Creates a clone of the property bag for a given message. This is useful for copying the property bag of one message to another. |
ValidatePropertyValue | Checks that the object is a valid property value. |
This class is unsupported and is
part of the BizTalk product infrastructure. If you run into trouble
using it, Microsoft Product Support may not support you. However, it is
common knowledge that it exists and makes pipeline component development
much easier. |