Often, people need to get
information from a database and submit it to BizTalk or have BizTalk
send it out to a third-party destination. The usual response for this is
to use the appropriate database adapter, generate the schemas for the
SQL statement or the stored procedure, and use some combination of an
orchestration/port and adapter to generate the data, publish it to the
Messagebox, and send it to the appropriate destination. While this
solution works, it often is met with a response like "But I just want to
call a stored procedure and have each row be sent to BizTalk as an XML
document."
Our solution to this scenario is called the
Databased Disassembler (yes, it is a pun on words as the data is based
on a database). The walkthrough for how this solution works is as
follows:
A receive pipeline is created that hosts the custom Disassembler component.
The Disassembler only accepts a primer message. A primer message
is a message that contains all the parameters needed for the pipeline
component to begin executing. It is a primer because it is a message
that gets the process going, or "primes" it. The message itself contains
data that is not meaningful to anyone but the Databased Disassembler
pipeline component.
The
pipeline component examines the primer message and retrieves all the
parameters needed for execution. These parameters can include
The connection string for the database
The stored procedure name
The parameters needed to call the stored procedure
The resulting schema type for the returned data
The
number of rows to include in an output message (The default is usually
1, but it can contain multiple documents wrapped in an envelope if so
desired.)
If an envelope is requested, the XPath to the body element in the envelope as well as the schema name for the envelope
If
no values exist for a given parameter, the pipeline component can have
them defaulted to a value when it is placed in the pipeline surface
designer.
Once
all the parameters are gathered, the component calls the stored
procedure with the parameters supplied in the primer message.
It creates a DataReader object from the result of the stored procedure call. If no records were returned, an exception is thrown.
If
more than one record is requested per created message, then the
component generates a new empty instance of the envelope schema that was
specified in the primer message. If only one record is requested, then
no envelope is used. If more than one document is requested per message
but no envelope schema is supplied, then an exception is thrown.
For
each row that is returned, a new but empty XML document is created
based on the requested schema in the primer message.
Each element name in the blank XML instance must exist as a named column in the DataReader with the same name.
This way, the schema instance can change, and all that is required is
an update to the stored procedure. In this fashion, you have a logical
connection between your XML schema and your database schema.
The component continues to create new XML documents in the GetNext() method of the Disassembler interface until no more rows exist. When all rows have been processed, the component returns Nothing.
The following code shows the schema for the primer message that will be used to signify the start of the processes:
<?xml version="1.0" encoding="utf-16"?>
<xs:schema xmlns:b="http://schemas.microsoft.com/BizTalk/2003"
xmlns="http://TR3.Schemas.Primer" targetNamespace="http://TR3.Schemas.Primer"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="PrimerData">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="ConnectionString"
nillable="true" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="StoredProcedureName"
nillable="true" type="xs:string" />
<xs:element name="StoredProcParams" nillable="true">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="1">
<xs:element minOccurs="0" maxOccurs="unbounded" name="ParamValue"
nillable="true" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" default="1"
name="RecordsPerMessage"
type="xs:integer" />
<xs:element minOccurs="0" maxOccurs="1" name="OutputMessageSchema"
nillable="true" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="EnvelopeSchema"
nillable="true" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="BodyXPath" nillable="true"
type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Figure 1 gives a pictorial representation of the message flow that will take place for this process to work. Note one key element in Figure 1.
The data from which the Disassembler gets its rows is not the BizTalk
Messagebox. This is a key feature of this pattern. Since the database
can be essentially "anything," it becomes trivial to get data from any
OLEDB-compliant data source such as Oracle, DB2, and Sybase. This
technique only requires writing a stored procedure and getting a
connection string to access the data source. There is no need to
purchase additional adapters for this solution to be used across
different databases. This is a keyselling point for many projects that
might want to use this approach.
Another key architectural
component of this solution is that fact that a Disassembler is used to
create the messages. Each of the messages that are created from a unique
primer message will be tagged as being from the same Interchange. This
means that every message that was created from this primer message will
have the same InterchangeID. This is very useful, as it allows
you to easily correlate all the messages in an execution flow within an
orchestration using a convoy pattern, because they will all have the
same InterchangeID. The only issue becomes how to signify that
the message received is the last in the sequence. You will need to create a custom property schema that is associated to
each of the messages that is created by the Disassembler. Each message
will be tagged with a sequence number, nd there will be a Boolean flag
in the schema that indicates the last message in the sequence. This flag
is then used to signal the orchestration that the last message has been
received in the convoy.