Hi all
Some weeks ago, I had a customer that had an Order XML and needed to fetch all the names of the ordered items based on the item number that was in the XML.
He contacted me because the solution he had thought of didn’t work. What he did was that he mapped the Order XML to a SQL Adapter schema that called an SP in SQL Server to get the item name based on the item number. The issue he ran into was, that the SP got called multiple times – once for each item, and the SQL adapter didn’t seem to batch all the results into one result for his orchestration.
So, given this simplified Order:
<ns0:Order xmlns:ns0="http://MultipleCallsToSP.Order">
<Header>
<CustomerName>CustomerName_0</CustomerName>
<OrderNumber>OrderNumber_0</OrderNumber>
</Header>
<OrderLines>
<OrderLine>
<ItemNumber>21</ItemNumber>
<Quantity>42</Quantity>
</OrderLine>
<OrderLine>
<ItemNumber>42</ItemNumber>
<Quantity>21</Quantity>
</OrderLine>
</OrderLines>
</ns0:Order>
He mapped it to this XML:
<ns0:GetItemNameRequest xmlns:ns0="http://eliasen.dk">
<ns0:GetItemName ItemID="21" />
<ns0:GetItemName ItemID="42" />
</ns0:GetItemNameRequest>
This was then sent to SQL Server using the SQL Adapter to call a SP named “GetItemName” which just takes an ItemID (int) as parameter and returns the ItemName hat matches the ItemID.
Now, the schema that is generated for the SQL Server request actually doesn’t allow for multiple GetItemName elements to be created, but that is changeable If you set it to have maxOcurs = unbounded, then it can occur multiple times, and what happens is that the SP is called multiple times. Unfortunately, only one of the ItemNames is returned – the rest is ignored.
So the customer came to me because naturally, he needed all the ItemNames and not just one of them. I have suggested 5 possible solutions, which I will describe here.
First option
Use the pattern described at http://blog.eliasen.dk/2006/11/05/LoopingAroundElementsOfAMessage.aspx to loop around the order lines and build the resulting XML one order line at a time.
Second option
Use enveloping in the receive location in order to get one orchestration started for each order line.
Third option
Use the Database Lookup functoid to retrieve the ItemName based on the ItemID
Fourth option
Generate a comma separated list of ItemID’s in the map, and let the stored procedure use that list to return the relevant ItemNames. This has some consequences for the stored procedure. Before it looked like this:
SELECT ItemNumber, ItemName
FROM Items
WHERE Items.ItemNumber = @ItemID
FOR XML AUTO, ELEMENTS
Now, it looks like this:
SELECT ItemNumber, ItemName
FROM Items
WHERE EXISTS (select * from dbo.Split(‘,’,@items) where [Items].ItemNumber = ID)
FOR XML AUTO, ELEMENTS
@items is the parameter for the SP, which is just an nvarchar that is to contain the comma separated list.
For this to work you need the Split function, which looks like this:
CREATE FUNCTION [dbo].[Split] (@sep char(1), @s varchar(512))
RETURNS table
AS
RETURN
(
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(@sep, @s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT pn, CONVERT(int, SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END)) AS ID
FROM Pieces
)
In order to generate the comma separated list in your map, I have written two blog posts about this issue, which you can find at http://blog.eliasen.dk/2009/06/22/HandlingCommaSeparatedValuesInsideAMapPartI.aspx and http://blog.eliasen.dk/2009/06/27/HandlingCommaSeparatedValuesInsideAMapPartII.aspx
Fifth option
The fifth and last option i want to mention is, that with the new SQL Server LOB adapter from Adapter Pack 2.0, it appears that you can do it like the customer wanted to do it in the first place with sending one XML to SQL Server and getting an accumulated response back from SQL Server based on several calls to a stored procedure. I haven’t had time to test this, but look out for another blog post about this
Hope this helps someone.
--
eliasen