Kurz zur Erinnerung: Zum Debatchen genügt es, bei einem Schema die Property 'Envelope' auf 'yes' zu setzen und in der Property 'Body XPath' den übergeordneten Knoten der auszupackenden Messages anzugeben, um die XML-ReceivePipeline anzuweisen, alle Elemente unterhalb dieses Knotens als neue Message auszupacken und in der MessageBox abzulegen.
Leider ist das Einpacken nicht so einfach zu erledigen. Da muss man schon selbst Hand anlegen.
Den meisten wird das Verfahren 'Sequential Convoy' bekannt sein. Hier soll es allerdings nicht um den Convoy gehen, sondern um das Verfahren, einzelne Messages in einen Umschlag zu bekommen.
Variante 1 – Mapping
Zwei InputMessages eine OutputMessage:
Input
Process
Output
SammelMessage (n)
MassCopy
SammelMessage (n+1)
SingleMessage
MassCopy
Dabei wird die OutputMessage im nächsten Durchgang zur neuen InputMessage. Der Nachteil dieser Lösung besteht darin, dass mit einem Mapping lediglich ein Messagetype verarbeitet werden kann.
Variante 2 – XML Manipulation in Memory
Das Prinzip kurz und knapp:
Ein beliebiger Knoten einer konstruierten Message kann an einen beliebigen Knoten einer SammelMessage anghängt werden.
SingleNode:
XmlDocument xmlSourceDoc, xmlTargetDoc;
XmlElement parentElement,ChildElement;
string sourceChildNodesXPath, targetParentNodeXPath;
sourceChildNodesXPath = "//*[local-name()='Order']";
targetParentNodeXPath = "//*[local-name()='Body']";
xmlSourceDoc = "<Orders>
<Order>
<Nummer>1</Nummer>
</Order>
</Orders>"
xmlTargetDoc = "<Envelope>
<Header>
<Party></Party>
<URL></URL>
</Header>
<Body></Body>
</Envelope>"
parentElement = (XmlElement)targetDocument.DocumentElement.SelectSingleNode(targetParentNodeXPath);
childElement = (XmlElement)sourceDocument.DocumentElement.SelectSingleNode(sourceChildNodesXPath);
parentElement.AppendChild(childElement);
oder
parentElement.InnerXml = parentElement.InnerXml + childElement.OuterXml;
NodeList:
XmlDocument xmlSourceDoc, xmlTargetDoc;
XmlElement parentElement;
XmlNodeList childElements;
string sourceChildNodesXPath, targetParentNodeXPath;
sourceChildNodesXPath = "//*[local-name()='Order']";
targetParentNodeXPath = "//*[local-name()='Body']";
xmlSourceDoc = "<Orders>
<Order>
<Nummer>1</Nummer>
</Order>
<Order>
<Nummer>2</Nummer>
</Order>
<Order>
<Nummer>3</Nummer>
</Order>
<Order>
<Nummer>4</Nummer>
</Order>
</Orders>"
xmlTargetDoc = "<Envelope>
<Header>
<Party></Party>
<URL></URL>
</Header>
<Body></Body>
</Envelope>"
parentElement = (XmlElement)targetDocument.DocumentElement.SelectSingleNode(targetParentNodeXPath);
childElements = (XmlElement) sourceDocument.SelectNodes(sourceChildNodesXPath);
foreach (XmlElement n in childElements)
{
parentElement.AppendChild(n);
}
oder
foreach (XmlElement n in childElements)
{
parentElement.InnerXml = parentElement.InnerXml + n.OuterXml;
}
Im BizTalk muss man natürlich die foreach -Schleife mit einer Loop umsetzen.
Da die .NET Klassen XmlElement und XmlNodeList non-serializable sind, ist es notwendig die Aktion mit einem 'atomic Scope' zu umschließen. Die 'SingleNode'-Variante kann in einem ExpressionShape umgesetzt werden.
Es ist allerdings empfehlenswert, den aufgezeigten Vorgang in eine Helper-Methode auszulagern.
Dann könnte man den ganzen Vorgang mit dem folgenden Call ausführen:
sourceChildNodesXPath = @"//*[local-name()='Order']";
targetParentNodeXPath = @"//*[local-name()='Body']";
xmlTargetDoc = MessageAggregator.aggregateElements
(xmlSourceDoc, xmlTargetDoc, sourceChildNodesXPath, targetParentNodeXPath);
Nachdem alle Messages eingepackt wurden, braucht man das xmlTargetDoc nur noch der typisierten Message zuweisen und versenden.
Variante 3 – XML Manipulation on Disk
Diese Variante stammt noch aus der Rubrik „Not macht erfinderisch“ aus dem Jahre 2006 von Richard Seroter
https://blogs.msdn.microsoft.com/richardbpi/2006/05/08/biztalk-aggregation-pattern-for-large-batches/
und ist insbesondere für sehr große oder sehr viele Messages interessant, wenn Mappings und Speicher zum Problem werden.
Wen es interessiert: Einfach den Beitrag unter der obigen Adresse ansehen.
Andreas ConradSenior Developer