Sunday, 05 November 2006

Hi

A guy on the microsoft.public.biztalk.orchestration newsgroup has asked about looping around elements inside an orchestration, and I thought I'd just write some lines about the issue here, for everybody to see in the future.

The problem is, that he has the following document structure:

<Employees>
<Employee title="mgr">
</Employee>
<Employee title="vp">
</Employee>
<Employee title="ceo">
</Employee>
</Employees>

And he wants to do something with each employee, based on what the title is. As I see it, there are two options:

  1. Loop through the Employee-elements inside the orchestration
  2. Use an envelope to split the incoming Employees-message into several Employee-messages

Suggestion 1

I have proposed the following two schemas:

 for the big message that arrives and  for the individual Employee. Note, that I have promoted "title" as a distinguished field.

I then create an orchestration, that takes an instance of the Employees schema as input, and loop around the employees. The orchestration looks like this:

It receives the incoming message, and then initializes a couple of variables. The first expression shape, named "Initialize Loop variables" contains the following three lines:

empCount = xpath(InputMessage, "count(/*[local-name()='Employees' and namespace-uri()='']/*[local-name()='Employee' and namespace-uri()=''])");
counter = 1;
counterStr = "1";

Basically, I get the number of employee-elements, and initialize the counter. The stringcounter is used to build xpath expression later, as you will see.

Then, I loop. The loop condition is "counter <= empCount" - so I want to loop as long as there are employees.

Inside the scope, I have declared a message employeeMessage, which is of the type of a single employee.

In the message assignment shape, I do this:

EmployeeMessage = xpath(InputMessage, "/*[local-name()='Employees' and namespace-uri()='']/*[local-name()='Employee' and namespace-uri()=''][" + counterStr + "]");

It takes the employee from the big incoming message that corresponds to the counter and assigns it to the message that is to be constructed.

In the next expression shape, I just write the content of the distinguished field "title" of the employee to the eventlog.

And in the final expression shape, I increment the counter variables:

counter = counter + 1;
counterStr = System.Convert.ToString(counter);

So by doing this, I am looping over the employees inside the employees-message, and I have access to all the values inside the single employee. I didn't have to have schema number 2, describing a single employee, I could have just used xpath all the way down, or maybe declare an XmlNode variable to hold the employee-element instead. But I like this solution better.

Another option I have now is that I can add a decision shape, and based on the title value, I can call different orchestrations, that will handle a specific employee-type. Or perhaps I could just send the employee message to a direct-bound send port and have other orchestrations subscribe to the employee message type. This would require the title to be a promoted property instead of a distinguished field, though, in order to route on it.

Suggestion 2

I propose using an XML Envelope to split the incoming file into several employee-messages and let the orchestration handle them individually.

To do this, I have created two schemas:

 for the envelope and  for the employee. Note: I have changed the names of the root nodes in both schemas. Naturally, in real life you wouldn't do this. The only reason I did this is to be able to have both my examples deployed at the same time. If I hadn't done it, I would have had multiple schemas deployed with the same combination of target namespace and root node, which we all know is BAD. On the schema for the envelope, I have clicked on the "<Schema>"-node and in the properties windows, I have set "Envelope" to "Yes". Then, I clicked on the "EmployeesEnvelope"-node and in the properties window, I set the "Body XPath" property to point at the "EmployeesEnvelope"-element.

Then, I created an orchestration, that takes an employee as the input - not the employees-type, but the single employee. It looks like this:

So here I have a much smaller, simpler, and faster orchestration than the one from suggestion 1.

After the solution is deployed, I create a receive location, and remember to use the default XMLReceive pipeline. The disassembler stage will look at the incoming message, determine the message type, see it is an envelope, split the message into smaller messages, and publish them individually with their own message type.

If I need different orchestrations depending on the value of the title attribute, I can promote it to a promoted property instead of a distinguished field, and add it to a filter on the receive shape in each orchestration.

Examples:

I have my two samples here: LoopAroundEmployee.zip (68,66 KB) and here: LoopAroundEmployeeEnvelope.zip (48,64 KB)

I hope this has been useful for someone. If you have any questions, just ask.

--

eliasen

Sunday, 05 November 2006 14:04:11 (Romance Standard Time, UTC+01:00)  #    Comments [13]  | 
Wednesday, 25 April 2007 15:42:30 (Romance Daylight Time, UTC+02:00)
Thanks! This example was very helpful.
John
Thursday, 26 April 2007 11:10:37 (Romance Daylight Time, UTC+02:00)
Anytime!
Wednesday, 22 August 2007 11:21:05 (Romance Daylight Time, UTC+02:00)
<b>Thanks!</b> was looking for a way to loop around the order lines of a web order and create a new message from each - this looks like the way to do it :-) just about to try it out now...
Wednesday, 19 September 2007 16:36:41 (Romance Daylight Time, UTC+02:00)
Just wondering if this works when EmployeeFromEnvelope is a flat file schema. i am about to try it now...
KVK
Wednesday, 19 September 2007 21:04:09 (Romance Daylight Time, UTC+02:00)
Hi

Sure it does. Inside an orchestration, everything is XML, so it will work in exactly the same way.

You can't use an envelope for flat file. For flat file it is called a header schema, body schema and trailer schema. Other than that, they can also be used to split up a flat file into several XML documents.

--
eliasen
Thursday, 27 September 2007 15:08:02 (Romance Daylight Time, UTC+02:00)
Thank you so much! I've just discovered your blog, and your posts are the pillow between my head and the desk as I struggle to learn BizTalk... ;)
Thursday, 27 September 2007 19:40:28 (Romance Daylight Time, UTC+02:00)
Hi

Thanks for the kind words. It is always nice to have ones work appreciated :-)

--
eliasen
Thursday, 26 February 2009 13:31:49 (Romance Standard Time, UTC+01:00)
Hi,
Please i'm trying to apply your loop logic, but i get some xpath error like that :

Event Type: Error
Event Source: XLANG/s
Event Category: None
Event ID: 10034
Date: 26/02/2009
Time: 12:22:44
User: N/A
Computer: ALPHA-BIZTALK
Description:
Uncaught exception (see the 'inner exception' below) has suspended an instance of service 'DepotDemandes.Depot(ed088454-7dbd-2295-4855-1dd347aba1ac)'.
The service instance will remain suspended until administratively resumed or terminated.
If resumed the instance will continue from its last persisted state and may re-throw the same unexpected exception.
InstanceId: bbbe8a42-516f-451f-a576-6ff5fd502f17
Shape name: Initialize Loop variables
ShapeId: f7e9649a-dfb8-40b7-b23c-74a4caa512f0
Exception thrown from: segment 1, progress 10
Inner exception: 'count(/*[local-name()='Demandes']/*[local-name()='Demande']' has an invalid token.

Exception type: XPathException
Source: System.Xml
Target Site: MS.Internal.Xml.XPath.AstNode ParseMethod(MS.Internal.Xml.XPath.AstNode)
The following is a stack trace that identifies the location where the exception occured

at MS.Internal.Xml.XPath.XPathParser.ParseMethod(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParsePrimaryExpr(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParseFilterExpr(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParsePathExpr(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParseUnionExpr(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParseUnaryExpr(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParseMultiplicativeExpr(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParseAdditiveExpr(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParseRelationalExpr(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParseEqualityExpr(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParseAndExpr(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParseOrExpr(AstNode qyInput)
at MS.Internal.Xml.XPath.XPathParser.ParseXPathExpresion(String xpathExpresion)
at System.Xml.XPath.XPathExpression.Compile(String xpath, IXmlNamespaceResolver nsResolver)
at System.Xml.XPath.XPathNavigator.Evaluate(String xpath)
at Microsoft.XLANGs.Core.Part.XPathLoad(Part sourcePart, String xpath, Type dstType)
at DepotDemandes.Depot.segment1(StopConditions stopOn)
at Microsoft.XLANGs.Core.SegmentScheduler.RunASegment(Segment s, StopConditions stopCond, Exception& exp)


can you help me plz !
Thursday, 26 February 2009 20:16:04 (Romance Standard Time, UTC+01:00)
Hi

Well, right now, the only thing I can ses is that the string "count(/*[local-name()='Demandes']/*[local-name()='Demande']" should be "count(/*[local-name()='Demandes']/*[local-name()='Demande'])" instead. Try that. If that fails, try sending me your schema and orchestration.

--
eliasen
Thursday, 07 January 2010 12:59:10 (Romance Standard Time, UTC+01:00)
very interesting post. I downloaded both and they worklike a charm. However, I have one question regarding 2nd suggestion. How Biztalk messaging engine recognizes that it should use the enveloppe schema? there is no apparent link between both schemas, in orchestration, receive shape is receving a message of type Employee where in the physical port we are submitting an instance of type EmployeesEnvelope.
During my work with BTS, I always had messages suspended if they don't correspond to schema expected by the receive shape, so how this receive shape is accepting a different schema?
thanks in advance
Thursday, 07 January 2010 18:59:49 (Romance Standard Time, UTC+01:00)
Hi Salam

When creating a schema, there is a property for the schema called "Envelope". If this property is set to "true", the XML disassembler will automatically treat the incoming message as an envelope message, extracting the inner documents as specified in the "Body XPath" property in the envelope schema. The disassembler will extract the inner documents and submit them on at the time.

Hope this helps?
Thursday, 07 January 2010 20:19:13 (Romance Standard Time, UTC+01:00)
Understood, this what I have seen. My question is how pipeline becomes aware that he should apply this envelope, no config has been doen except as you said setting a Flag on a schema which is not referenced by the receive shape and the Boday xpath? Do you mean that once we declare an enveloppe schema inside a Biztalk assembly, each xmlreceivepipeline will automatically apply it without the need to do any config? I hope this make it more clear about my query.
regards
Friday, 08 January 2010 20:38:29 (Romance Standard Time, UTC+01:00)
The disassembler looks at the incoming XML and finds the schema that matches the message type of the XML.
If this schema has the "Envelope" property set to true, the disassembler treats the incoming XML as an envelope and starts debatching the XML.

So yes, no confguration necessary. The envelope must just be marked as an envelope and the disassembler does the rest. It will publish each of the messages inside the envelope one at the time with the message type that matches the debatched message promoted, which will then cause the orchestration to pick it up.
All comments require the approval of the site owner before being displayed.
OpenID
Please login with either your OpenID above, or your details below.
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, b, blockquote@cite, em, i, strike, strong, sub, sup, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview

Theme design by Jelle Druyts