BlackCatBone

Greetings,

We are using the SQL Adapter to call a stored procedure that inserts a row in a SQL Server table. One of the table columns is of type xml.

We want to pass the "raw" XML that is input through our receive port location. The schema for this location has a record named <root> with 5 elements beneath it - pretty simple stuff. We want to pass the native XML coming through the receive port to our back-end stored procedure. We need to store the original XML in the xml column for historical reference.

We have mapped the <root> record to an attribute in the send port schema and set the "Source Links" property on the mapping link to "Copy text and subcontent value". This causes the text values for the 5 elements beneath <root> to be concatenated. It works in the sense that our back-end database table is being updated with the value we pass to the stored procedure but it isn't the native XML we need.

How do we map the literal XML message to an output schema so that it will be passed through the SQL Adapter to a stored procedure

Thanks,

BCB



Re: BizTalk R2 General Need to pass "raw" input message to SQL Adapter - how?

Leonid Ganeline - MVP

If we need to pass ONE row to the SQL storproc, it is pretty easy.
Just map the XML message (inside BizTalk) elements to the service SQL request schema elements. This schema created by SQL Port wizard when we point it to the SQL storproc.
More interesting problem is to pass several rows to the SQL port. (We can use the updategramm, but you can see this approach with all description in the BizTalk Help and I don't want to repeat it. Here I want to describe using SQL storproc.)
We cannot pass the rowset to the storeproc as a parameter, but we can use different technics.
Let's serialize the rowset to the XML.
We pass this xml to the storeproc as one parameter with Text type. (Like string "<ns0:RootNode xmlns:ns0="http://MyNamespace">
<ElemName>...")
Inside store proc we use the OPENXML to deserialize this XML-text back to rowset.
Moreover we can serialize not only the rowset but any DataSet, say Order row and all OrderDetail rows in one Text parameter.
Below is the key source text:
=========================================================
text of the expression in the orchestration:
-------------------------------------
var_XmlDocument.LoadXml( "<ns0:Insert_to_Table_Request xmlns:ns0=\"http://....Schemas.Insert_to_Table\"><ns0:Insert_to_Table Xml_Text=\"\" /></ns0:Insert_to_Table_Request>" );
msg_BTS_ProcessEDIDocumentRequest.parameters = var_XmlDocument; // var_XmlDocument is the "<NewDataSet><Table><railway>CNsd</railwa..."
var_XmlDocument = msg_EDICanonical;
msg_BTS_ProcessEDIDocumentRequest.parameters.BTS_ProcessEDIDocument.Xml_Text = var_XmlDocument.OuterXml;
=========================================================
text of the storeproc:
-------------------------------------
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Insert_to_Table]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[Insert_to_Table]
GO
--================================================================================
-- Created: *******
--================================================================================
CREATE PROCEDURE dbo.Insert_to_Table
(
@Xml_Text TEXT
)
AS
SET NOCOUNT ON
-------------------------------------------------------------------------
If ( @Xml_Text Like ' ' Or @Xml_Text Like '' )
Return 0
-- ======================================= Variables: =======================================
Declare
---------------- Internal:
@i int,
@CurrentDate datetime,
@isTransactionStart int,
-- error handling:
@ErrorLogContext varchar(255),
@ErrorLogMessage varchar(500),
@EDI_History_Action varchar(50)
----------------- Temp table
Declare @Table table
(
railway varchar(50) NOT NULL
,voyage varchar(50) NOT NULL
,vessel_name varchar(50) NOT NULL
,vessel_arrive datetime NOT NULL
,vessel_depart datetime NOT NULL
,_x0032_0_count int NOT NULL
,_x0034_0_count int NOT NULL
,container_count int NOT NULL
,teu real NOT NULL
,feet int NOT NULL
)

---------------- Initializing:
set @ErrorLogContext = 'Insert_to_Table: '
--=========================== Converting XML to the Variable-tables:
-- Variable-tables have the same structure as XML records .
-- Properties Null/NotNull set up here when data is converting from XML to the Variable-tables.
set @isTransactionStart = 0
-- Calling a system stored procedure to prepare the document for calling OPENXML
set @ErrorLogMessage = 'Error converting XML to @i '
EXEC sp_xml_preparedocument @i OUTPUT, @Xml_Text, '<NewDataSet/>'
if @@error <> 0 goto ON_ERROR
-- preparing the common part of Xml document:
-- Xml variables:
set @ErrorLogMessage = 'Error Insert @Table '
Insert @Table
SELECT
isNull(railway, '')
,isNull(voyage, '')
,isNull(vessel_name, '')
,vessel_arrive
,vessel_depart
,_x0032_0_count
,_x0034_0_count
,container_count
,teu
,feet
FROM
OPENXML(@i, '/NewDataSet/Table', 2)
WITH
(
railway varchar(50)
,voyage varchar(50)
,vessel_name varchar(50)
,vessel_arrive datetime
,vessel_depart datetime
,_x0032_0_count int
,_x0034_0_count int
,container_count int
,teu real
,feet int
)
if @@error <> 0 goto ON_ERROR
set @ErrorLogMessage = 'Error EXEC sp_xml_removedocument '
EXEC sp_xml_removedocument @i
if @@error <> 0 goto ON_ERROR

-- ========================================================================
-- Transfer data from the Variable-table to DB:
-- This SP starts from the BizTalk SQL port which create transaction
-- in this case we have to handle Rollback case accordingly.
declare @MyTransaction int
set @MyTransaction = 0
If @@Trancount = 0
Begin
Begin Transaction MAIN
Set @MyTransaction = 1
End
set @isTransactionStart = 1
set @ErrorLogMessage = 'Error inserting into Table '
insert [Table]
select *
FROM @Table
if @@error <> 0 goto ON_ERROR
--==============================================================================================================
-- DONE:
If @MyTransaction = 1 and @@Trancount > 0
Begin
COMMIT work -- TRAN MAIN
End
RETURN 0
--==============================================================================================================
ON_ERROR:
If @MyTransaction = 1 and @@Trancount > 0
Begin
ROLLBACK work --TRAN MAIN
End
set @ErrorLogContext = 'Insert_to_Table: ' + @ErrorLogContext
return 1
/* test case:
declare @ret int
exec @ret = Insert_to_Table '<NewDataSet><Table><railway>sd</railway><voyage>sdfsdfdfsdf</voyage><vessel_name>sdfgdfg</vessel_name><vessel_arrive>2007-06-30 06:34:00</vessel_arrive><_x0032_0_count>17</_x0032_0_count><_x0034_0_count>8</_x0034_0_count><container_count>25</container_count><teu>33.000000</teu><feet>450</feet></Table></NewDataSet>'
select @ret -- return 0
*/

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
=====================================================================

--

Regards,

Leonid Ganeline
BizTalk Solution Developer [MVP]
----------------------------------------------------------------------------------
Microsoft Certified Technology Specialist: BizTalk Server 2006
http://geekswithblogs.net/leonidganeline/





Re: BizTalk R2 General Need to pass "raw" input message to SQL Adapter - how?

BlackCatBone

Hello Leonid,

Thank you for your quick response!

Maybe I'm not understanding your reply and I probably didn't explain my situation well enough, so let me add these details.

I'm processing an input message that looks like this:

<ns0:Root xmlns:ns0="http://SprocTest2.SprocTest2Input">

<CustomerName>CustomerName_0</CustomerName>

<Address>Address_0</Address>

<City>City_0</City>

<PostalCode>PostalCode_0</PostalCode>

<Region>Region_0</Region>

</ns0:Root>

My SQL Server table has this schema:

CREATE TABLE [dbo].[Shred](

[CustomerName] [nvarchar](30) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,

[Address] [nvarchar](60) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,

[City] [nvarchar](30) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,

[Region] [nvarchar](30) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,

[PostalCode] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,

[XmlBlob] [xml] NULL

) ON [PRIMARY]

The five elements in the input message are used to update the 1st 5 columns in the table via a stored procedure, and this all works correctly. The final table column - XmlBlob - is meant to hold the entire input message including the markup. (I basically want the OuterXml for the root node.) This is the part I don't know how to accomplish. I don't know how to get a "handle" to the entire input message and I don't see how I would map it to the output message attribute that corresponds to XmlBlob.

I'm so new to BizTalk that I squeak when I walk, so if you can break it down into simple terms, that would be helpful.

Thanks again,

BCB





Re: BizTalk R2 General Need to pass "raw" input message to SQL Adapter - how?

Leonid Ganeline - MVP

Make this element as distinguish property and use the expression like this:
XmlDocument = msg_Input;
msg_SprocTest2Input.XmlBlob = var_XmlDocument.OuterXml;





Re: BizTalk R2 General Need to pass "raw" input message to SQL Adapter - how?

BlackCatBone

Hi Leonid,

Thanks again for your help. I am new enough that I must ask for additional details.

How and where do I add the "distinguish property" you mentioned. Do I do this to the input schema, output schema, or the map Do I need to add a new element to the input schema to hold the OuterXml for the root node And how and where do I attach the C# code you provided above

Thanks,

BCB





Re: BizTalk R2 General Need to pass "raw" input message to SQL Adapter - how?

Leonid Ganeline - MVP

You're wellcome!
This code (from the last answer, the short one) you have to put to the Expression shape in orchestration.
You can assign the distinguished property from the Schema editor by right-click the schema node...
As I understand, yes, you have to add node to hold the OuterXml. This node you have to "distinguish".





Re: BizTalk R2 General Need to pass "raw" input message to SQL Adapter - how?

BlackCatBone

Hello Leonid,

I see light at the end of the tunnel but I'm still having a few problems making this work. This is what I've done:

1. Added a new element to my input schema and named it XmlBlob. (The same name used in the output schema.)

2. Promoted the new XmlBlob in my input schema to a "distinguished field".

3. Mapped XmlBlob in the input schema to XmlBlob in the output schema.

4. Created a variable within the orchestration named InputDocument (It is of type System.Xml.XmlDocument)

5. Added an "Expression" shape between the "Receive" shape and the "ConstructMessage" shape where the mapping occurs.

6. Added code to the new "Expression" shape as follows:

InputDocument = Message_1;

Message_1.XmlBlob = InputDocument.OuterXml;

---

The 1st line assigns the input message to the orchestration variable I created and appears to work.

The 2nd line is supposed to assign the Xml for the input message to the XmlBlob element in the input message. This line generates an intellisense error message 'Message_1:' Message construction must be done in a construct block.

I was trying to follow the guidance you provided in your previous response but clearly I've missed something. Can you give me another push in the right direction

Thanks again for your help.

BCB





Re: BizTalk R2 General Need to pass "raw" input message to SQL Adapter - how?

BlackCatBone

Here is my solution. The essence is that I had to create a third message in addition to the input and output messages I originally had. This third message is used to hold the raw xml from the input message.

These are my steps:

  1. Create a new message in the orchestration named msg_NotesAuditContext.
  2. Create a new orchestration variable named var_NotesAuditContext of type System.Xml.XmlDocument. It will be used to get a handle to the msg_NotesAuditContext.
  3. Create a new orchestration variable named var_NotesAuditIn of type System.Xml.XmlDocument. It will be used to get a handle to msg_NotesAuditIn, the input message.
  4. Add a new Expression shape immediately following the Receive shape that receives input message msg_NotesAuditIn. This Expression shape was inserted between the original Receive and Transform shapes. The code is below.
  5. Add a new Message Assignment shape immediately following the Expression shape added in the previous step. The code is listed below. The Message Assignment is followed by the original Transform shape.
  6. Double-click the Transform shape to bring up the Transform Configuration window. Change the Source Transform so that it lists both msg_NotesAuditIn and msg_NotesAuditContext as "sources".
  7. The map now shows both messages. Map the XmlBlob element to the output message.

Expression code (step 4):

var_NotesAuditIn = msg_NotesAuditIn;

Message Assignment code (step 5):

var_NotesAuditContext.LoadXml("<ns0:Root xmlns:ns0='http://NotesAudit.NotesAuditContext'><XmlBlob>XmlBlob_0</XmlBlob></ns0:Root>");

msg_NotesAuditContext = var_NotesAuditContext;

msg_NotesAuditContext.XmlBlob = var_NotesAuditIn.OuterXml;

I got this technique from the Apress book "BizTalk 2006 Recipes: A Problem-Solution Approach", a very handy reference.





Re: BizTalk R2 General Need to pass "raw" input message to SQL Adapter - how?

Leonid Ganeline - MVP

BlackCatBone wrote:

5. Added an "Expression" shape between the "Receive" shape and the "ConstructMessage" shape where the mapping occurs.

Hi,
All seems OK, but...
This is a mistake.
You should place this code in the Assign shape and place this shape INSIDE Construct shape AFTER Transfer shape with mapping.
It's the same as in book, but you don't have to create a separate message to store the Xml text and the map two messages to one.





Re: BizTalk R2 General Need to pass "raw" input message to SQL Adapter - how?

BlackCatBone

Hello Leonid,

Can you please explain why you say "this is a mistake" My BizTalk application is working fine so I'm wondering if you mean that there is a cleaner, more straightforward way to accomplish my goal. But I'd appreciate hearing why my approach may be mistaken.

Thanks,

BCB





Re: BizTalk R2 General Need to pass "raw" input message to SQL Adapter - how?

Sheng Jiang

From Biztalk documentation:

You specify the message variable that you want to construct, and make assignments to the message or its parts. All assignments to any given message must take place within the same Construct Message shape.

If you want to modify a property on a message that has already been constructed¡ªsuch as a message that has been received¡ªyou must construct a new message instance by assigning the first to the second and then modifying the property on the new message instance; both the construction and modification occur within the same Construct Message shape.

So you should not modify a message once it is constructed.

Reference

A message that is outside a Construct Message shape may not update in BizTalk Server 2004