Broadcast- und Unicast-Flatfile Verarbeitung in BizTalk

13.11.2017

Broadcast- und Unicast-Flatfile Verarbeitung in BizTalk

oder wie baue ich eine dynamische Flatfile Disassembler Pipeline Komponente?

Folgendes Problem:

Der Kunde hat ein zentrales ERP System (ERP) und mehrere Lagerverwaltungssysteme (LVS).

Die Systeme sollen über Flatfile Nachrichten kommunizieren (von ERP an die LVS und umgekehrt).

Eine Flatfile Nachricht kann unterschiedliche Satzarten enthalten.

Dabei gibt es Satzarten, die von ERP an alle LVS gesendet werden sollen (Broadcast) und andere Satzarten, die lediglich an ein LVS gesendet werden sollen (Unicast).

Eine Flatfile Nachricht von ERP kann sowohl Broadcast als auch Unicast Satzarten enthalten (gemischte Nachricht).

 

Eine gemischte eingehende Nachricht muss also in mehrere ausgehende Nachrichten zerteilt werden:

-          0 bis 1 Broadcast Nachricht

-          0 bis n Unicast Nachrichten, maximal für jedes LVS eine.

 

Es gibt ca. 30 Satzarten.

Die Satzarten sind einfach:

-          Jede Satzart ist über ein Tag (erstes Feld) identifizierbar.

-          Für Unicast Satzarten steht im zweiten Feld die ID des Zielsystems.

-          Für Broadcast Nachrichten ist dies nicht der Fall.

 

Folgender Lösungsansatz:

Die „Entmischung“ der Satzarten in einer Eingangsdatei soll in einer Pipeline Komponente erfolgen.

Die Pipeline soll aus einer Eingangsnachricht mehrere Nachrichten ausgeben.

Im einfachste Fall sollen BizTalk SendPorts die Verteilung der Nachrichten übernehmen.

Details:

Eigener Disassembler

Da die Pipeline mehrere Nachrichten ausgeben soll, muss eine Disassembler Pipeline implementiert werden.

Dies geschieht durch Implementierung des Interfaces IDisassemblerComponent.

Dieses Interface fordert die Methoden Disassemble() und GetNext().

Disassemble erlaubt Vorbereitungen für die Zerteilung der Eingangsnachricht und GetNext gibt jeweils eine Nachricht zurück. BizTalk ruft GetNext so lange auf, bis null anstatt einer Nachricht zurückgegeben wird.

Eigener Flatfile Disassembler

Bis hier her war der Lösungsansatz „geradeaus“, aber wir würden ja gerne nicht nur Nachrichten zerteilen, sondern das eingehende Flatfile in Xml wandeln, da BizTalk intern nur mit Xml Nachrichten agieren kann.

Das „Standardvorgehen“ (ziehe Flatfile Disassembler PipelineComponent in Custom Pipeline, setze das Flatfile Schema etc.) funktioniert hier nicht, da wir ja unseren eigenen Disassembler entwickeln wollen. Glücklicherweise gibt zu diesem Zweck die Klasse FFDasmComp, welche von unserer Pipeline Komponente instanziiert und zur Flatfile Disassemblierung verwendet werden kann.

Da das Schema nicht im Pipeline Editor konstant gesetzt werden kann, muss entsprechende Information der FFDasmComp mitgeben werden.

Dies geschieht über das IProbeMessage Interface.

Die gesamte Verarbeitungssequenz ist folgende:

-          IProbeMessage.Probe

-          IDisassemblerComponent.Disassemble

-          IDisassemblerComponent.GetNext

Unsere Pipeline Komponente wird wie folgt arbeiten:

-          In IProbeMessage.Probe wird die Eingangsnachricht zerlegt. Hierbei werden eigene Datenstrukturen erzeugt (Für jede ausgehende Nachricht eine Klasseninstanz). Diese Klassen können jeweils eine BizTalk Flatfile Nachricht erzeugen und instanziieren je einen Flatfile Disassembler (FFDasmComp). Für die erzeugten Nachrichten wird das Property DocumentSpec gesetzt und für FFDasmComp.Probe aufgerufen. Damit wissen die erzeugten FFDasmComp, welchen Nachrichtentyp sie verarbeiten sollen.

-          In IDisassemblerComponent.Disassemble wird für jede FFDasmComp FFDasmComp.Disassemble für die erzeugte entmischte Flatfile Nachricht aufgerufen.

-          In IDisassemblerComponent.GetNext wird jeweils für eine FFDasmComp FFDasmComp.GetNext aufgerufen und die erzeugte Xml Nachricht zurückgeliefert.

 

Code Details

 

Die Interfaces wurden über mehrere Dateien verteilt.

    public partial class DynamicFFDisassembler : IProbeMessage

    {

        public bool Probe(IPipelineContext pContext, IBaseMessage pInMsg)

        {

            PContext = pContext;

            ReadIncomingMessageProperties(pInMsg);

            Stream stream = pInMsg.BodyPart.GetOriginalDataStream();

            DisassembleStream(stream);

 

            foreach (DynamicFFMessage message in Messages.Values)

            {

                message.FFDisAssembler.Probe(pContext, message.BtsMessage);

            }

 

            return true;

        }

    }

DynamicFFDisassembler ist unsere Disassembler Komponenten Implementierung. Die Probe Implementierung liest den Kontext der eingehenden Nachricht, da die ausgehenden Nachrichten diesen kopiert bekommen. DisassembleStream zerlegt die Eingangsnachricht und füllt das Messages Dictionary. Für jedes Ziel (Broadcast, LVS1, LVS2, etc.) wird hier eine Message (DynamicFFMessage) abgelegt. Message.BtsMessage enthält jeweils eine entmischte BizTalk Flatfile Nachricht mit kopiertem Nachrichtenkontext und gesetztem DocumentSpec Property.

DisassembleStream hat folgende Implementierung:

 

        public void DisassembleStream(Stream stream)

        {

            Messages = new Dictionary<string, DynamicFFMessage>();

            StreamReader reader = new StreamReader(stream);

            string singleRow = string.Empty;

            while ((singleRow = reader.ReadLine()) != null)

            {

                if (!string.IsNullOrEmpty(singleRow))

                {

                    SaveRowToDynamicMessages(singleRow);

                }

            }

            ...

 

            MessageKeys = Messages.Keys.OrderBy(x => x).ToList();

            foreach (DynamicFFMessage message in Messages.Values)

            {

                message.ToBTSMessage();

            }

        }

Es wird jede Zeile angesehen. SaveRowToDynamicMessages analysiert auf Satzart und Ziel. Die Zeile wird der Message mit dem entsprechenden Ziel zugeordnet.

Zuletzt werden für jede erzeugte Nachricht die BizTalk Nachricht erzeugt und die DocumentSpec zugewiesen. Die erstellte Flatfile Nachricht enthält eine zusätzliche Zeile mit der Satzart RoutingInfo. Hier ist der Zielsystem Identifier „Broadcast“, „LVS1“, „LVS2“ etc. enthalten.

        public void ToBTSMessage()

        {

            BtsMessage = PContext.GetMessageFactory().CreateMessage();

            IBaseMessagePart part = PContext.GetMessageFactory().CreateMessagePart();

            part.Data = ToStream();

 

            BtsMessage.AddPart("Body", part, true);

            foreach (string key in IncomingMessageProperties.Keys)

            {

                string[] nsComp = key.Split('‡');

                string strName = nsComp[0];

                string strNamespace = nsComp[1];

                if (key.EndsWith(false.ToString()))

                {

                    BtsMessage.Context.Write(strName, strNamespace, IncomingMessageProperties[key]);

                }

                else

                {

                    BtsMessage.Context.Promote(strName, strNamespace, IncomingMessageProperties[key]);

                }

            }

           

            string messageType = "http://qbq.wwslvs.biztalk#DynamicFF_WWS_LVS";

            DocSpec = PContext.GetDocumentSpecByType(messageType);

 

            BtsMessage.Context.Write("DocumentSpecName""http://schemas.microsoft.com/BizTalk/2003/xmlnorm-properties", DocSpec.DocSpecStrongName);

        }

Damit haben wir entmischte BizTalk Flatfile Nachrichten, die an FFDasmComp übergeben werden können.

Die verbleibende Arbeit ist damit einfach erledigt.

    public partial class DynamicFFDisassembler : IDisassemblerComponent

    {

 

        private string SourceFilename { getset; }

        private Dictionary<string, DynamicFFMessage> Messages { getset; }

        private List<string> MessageKeys { getset; }

        private Dictionary<stringobject> IncomingMessageProperties { getset; }

        public IPipelineContext PContext { getset; }

       

        public void Disassemble(IPipelineContext pContext, IBaseMessage pInMsg)

        {

            foreach (DynamicFFMessage message in Messages.Values)

            {

                message.FFDisAssembler.Disassemble(pContext, message.BtsMessage);

            }

        }

    }

Disassemble ruft für alle erzeugten Messages die zugeordneten FFDasmComp Instanzen auf.

Es verbleibt die Implementierung von GetNext:

        public IBaseMessage GetNext(IPipelineContext pContext)

        {

            if (Messages.Count <= 0)

            {

                return null;

            }

            IBaseMessage result = Messages[MessageKeys[0]].FFDisAssembler.GetNext(pContext);

            Messages.Remove(MessageKeys[0]);

            MessageKeys.RemoveAt(0);

            return result;

        }

 

Darin wird die nächste Nachricht vom FFDasmComp Disassembler in Xml gewandelt, aus der Liste Messages entfernt und an BizTalk geliefert.

 

Damit ist der spannende Teil der Anforderung erfüllt, es verbleibt noch eine Pipeline in den Sendports zu den LVS Systemen zu konfigurieren, die die RoutingInfo Zeile wieder entfernt und eine Flatfile Assembler Komponente zur Wandlung von Xml nach Flatfile aufruft.

 

Viel Spaß damit,

Thomas Kern

Lead Architect

zurück

© 2017 QUIBIQ GmbH · AGB und Nutzungsbedingungen