BizTalk: Maps mit Referenzen zu externen Assemblies debuggen

by wolfgang@gehirnwindung.de (Wolfgang) Mai 05, 2010 14:25

Um BizTalk-Maps zu debuggen, die eine exterene Assembly aufrufen, reichen die Standard-Möglichkeiten nicht mehr aus. Es erscheint die Fehlermeldung

Cannot find the script or external object that implements prefix 'http://schemas.microsoft.com/BizTalk/2003/ScriptNS0'

Um das zu umgehen, müssen die externen Assemblies dem Debugger bekannt gemacht werden. Um allgemein XSLT zu debuggen kann, neben der Möglichkeit dies über das Menü "XML" -> "Debug XSLT" zu erledigen, auch das Objekt XslCompiledTransform verwendet werden. Hierdurch ist man wesentlich flexibler und kann z.B. StartParameter angeben und/oder eben Extension Objects". Und da eine BizTalk-Map letztendlich auch nur ein XSLT-Dokument darstellt, gilt das auch dafür.

Als erstes sollte man ein C#-Projekt zur BizTalk-Solution hinzufügen. Darin wird lediglich unten stehende Methode eingefügt und die "Main"-Methode so angepasst, dass ein Aufruf der Methode "DebugXslt" mit den jeweiligen Parametern erfolgt.

/// <summary>
/// Helper Method to debug Maps with Extension Objects
/// </summary>
/// <param name="mapXslt">Path to xslt-file generated by the MapperCompiler (Validate Map)</param>
/// <param name="inputXml">Path to input-file (Test Map)</param>
/// <param name="extensionObjectsXml">Path to Extension Object XML (Validate Map)</param>
static void DebugXslt( string mapXslt, string inputXml, string extensionObjectsXml ) {
    if( !string.IsNullOrEmpty( mapXslt ) && !string.IsNullOrEmpty( inputXml ) && File.Exists( mapXslt ) && File.Exists( inputXml ) ) {
        XslCompiledTransform xslt = new XslCompiledTransform( true );

        // allow embedded scripts and document()-function
        XsltSettings xsltSettings = new XsltSettings( true, true );

        // Load the style sheet.
        xslt.Load( mapXslt, xsltSettings, null );

        // resolve extension objects
        XsltArgumentList xsltArgs = new XsltArgumentList( );
        if( !string.IsNullOrEmpty(extensionObjectsXml) && File.Exists( extensionObjectsXml ) ) {
            XmlReaderSettings rsettings = new XmlReaderSettings( );
            rsettings.IgnoreComments = true;
            rsettings.IgnoreProcessingInstructions = true;
            rsettings.IgnoreWhitespace = true;

            using( XmlReader reader = XmlReader.Create( extensionObjectsXml, rsettings ) ) {
                reader.ReadStartElement( "ExtensionObjects" );

                do {
                    string ns = "", an = "", cn = "";
                    while( reader.MoveToNextAttribute( ) ) {
                        switch( reader.LocalName ) {
                            case "Namespace":
                                reader.ReadAttributeValue( );
                                ns = reader.Value;
                                break;
                            case "AssemblyName":
                                reader.ReadAttributeValue( );
                                an = reader.Value;
                                break;
                            case "ClassName":
                                reader.ReadAttributeValue( );
                                cn = reader.Value;
                                break;
                        }
                    }

                    // load type and add instance to xslt-Arguments
                    if( !string.IsNullOrEmpty( ns ) && !string.IsNullOrEmpty( an ) && !string.IsNullOrEmpty( cn ) ) {
                        Type t = Type.GetType( Assembly.CreateQualifiedName( an, cn ) );
                        xsltArgs.AddExtensionObject( ns, System.Activator.CreateInstance( t, false ) );
                    }
                } while( reader.ReadToNextSibling( "ExtensionObject" ) );

                reader.Close( );
            }
        }

        // Execute the transformation.
        using( XmlWriter writer = XmlWriter.Create( TextWriter.Null ) ) {
            xslt.Transform( inputXml, xsltArgs, writer );
            writer.Close( );
        }

        xsltArgs.Clear( );
    }
}

Parameter Beschreibung
mapXslt Pfad zur XSLT-Datei
inputXml Pfad zur der Datei, die die Testdaten enthält
extensionObjectsXml

Pfad zur Datei, in der die Erweiterungsobjekte stehen (darf leer sein)

Nun müssen noch die einzelnen Pfade ermittelt werden. Mittels "Validate Map" (rot im Bild) werden die XSLT-Datei und die extensionObjects-Datei erstellt. Im Output-Window erscheinen die jeweiligen Pfade und müssen nur noch übernommen werden.
Hat man kein XML-Dokument, mit dem man Testen kann, sondern ein natives Format, so hilft "Test Map". Auch hier erscheint im Output-Window der Pfad mit dem Hinweis "Test Map used the following file".

 

Sind nun alle 3 Pfade ermittelt und eingetragen, kann es auch endlich losgehen. Damit der Debugger nicht einfach durchläuft, sollte die XSLT-Datei geöffnet und eine Haltepunkt eingefügt werden.

 

Nun muss das Projekt gestartet werden. Dazu mit der rechten Maustaste auf das Projekt zeigen und "Debug" -> "Start new instance" ausführen.
Alternativ das Projekt als StartUp-Project einrichten und F5 drücken...

Tags: , ,

Microsoft BizTalk Server

BizTalk Server Scripting-Functoid wider Erwarten...

by wolfgang@gehirnwindung.de (Wolfgang) September 23, 2009 20:00

Mit dem Scripting Functoid des BizTalk-Mappers kann man bekanntlich eigene Scripts in verschiedenen Sprachen (z.b. C#) während des Mappings ausführen lassen. Man definiert damit eine Funktion deren Parameter mit Hilfer der Eingangselemente gefüllt werden können. Der Rückgabewert der Funktion ist das, was das Functoid als Ausgangsparameter weitergibt. Bis hierhin ist alles noch selbsterklärend.

Falls nun mehrere Scripting Functoide existieren, die alle das Gleiche tun, so ist man gut beraten, auch immer den gleichen Methodennamen zu verwenden. Diese werden beim Kompilieren in XSLT zusammengeführt (aus dem *.btm-Dateien wird immer xslt generiert). Innerhalb dieser XSLT-Datei, wird dann auch immer nur eine Funktion aufgerufen.

Allerdings scheint den Entwicklern ein kleiner Fehler unterlaufen zu sein. Denn nicht der Methodenname und der dazugehörige Inhalt (MethodBody), sondern lediglich der Methodenname und die Anzahl der Parameter wird verwendet, um die "Gleichheit" zweier Funktionen festzustellen.

vollständigen Artikel anzeigen...

Tags: ,

Microsoft BizTalk Server

2. Parameter in Cumulative Functoids

by Wolfgang Kluge März 31, 2009 22:27

Gerne übersehen wird die Möglichkeit eines 2. Parameters in allen Cumulative Functoiden (Standardwert=0). Dieser gibt an, von welcher Ebene aus die zu kumulierenden Elemente "angeschaut" werden.

Folgendes XML-Dokuement angenommen.

<root>
    <nodes>
        <sub>
            <value>1</value>
            <value>1</value>
            <value>2</value>
        </sub>
        <sub>
            <value>3</value>
            <value>2</value>
            <value>2</value>
        </sub>
    </nodes>
</root>

vollständigen Artikel anzeigen...

Tags: ,

Microsoft BizTalk Server

Mehrere Looping Functoide auf dem gleichen Element

by Wolfgang Kluge März 30, 2009 23:43

Hätte ich ab und an gerne, bleibt aber wohl vorerst auch ein Punkt für die Wishlist.

Es geht nicht. Weder auf einer Seite, noch auf mehreren verteilt. Egal welche Einstellung oder welche Kombination aus diesen getroffen wird. Es geht einfach nicht ;)

Einer der beiden Loopings wird verwendet, der andere verworfen.

Tags: ,

Microsoft BizTalk Server

Sortiere Ausgabe im BizTalk-Mapper

by Wolfgang Kluge März 27, 2009 20:14

Einzelne Felder in der Ausgabe zu sortieren scheint eine gewöhnliche und vor allem alltägliche Aufgabe zu sein. Ist an sich auch unproblematisch innerhalb des BizTalk-Mappers. Im Looping-Functoid gibt man die Parameter einfach in der gewünschten Reihenfolge an.

Aber das geht nur, so lange man verschiedene Eingabe-Records nicht "mischen" will.
Ein kleines Beispiel:

<root>
    <nodes>
        <node id="1" />
        <node id="2" />
    </nodes>
    <subnodes>
        <subnode parent="1" />
        <subnode parent="1" />
        <subnode parent="2" />
    </subnodes>
</root>

Natürlich sollen in der Ausgabe nach der node mit der id 1 die dazu passenden subnodes erscheinen.
Und hier wirds nun schwerer - um nicht zu sagen mit den Basis-Functoiden (ausgenommen dem Script-Functioid) unmöglich.

Wenn man einfach nur sein Looping-Functoid wie gewohnt mit den Records nodes und subnodes verbindet, erscheint in der Ausgabe (wie erwartet)

<root>
    <NodeOrSubNode id="1" />
    <NodeOrSubNode id="2" />
    <NodeOrSubNode parent="1" />
    <NodeOrSubNode parent="1" />
    <NodeOrSubNode parent="2" />
</root>

vollständigen Artikel anzeigen...

Tags: ,

Microsoft BizTalk Server

Table Looping zusammen mit Logical Functoid

by Wolfgang Kluge März 24, 2009 18:38

Nachdem ich ein wenig an einem Table-Looping "rumgespielt" hatte, funktionierte auf einmal gar nichts mehr. Der Build-Vorgang brach mit der Meldung

XSL transform error: Unable to write output instance to the following <file:///../test_output.xml>. XSLT compile error at (40,38).
See InnerException for details. 'userCSharp:LogicalNe(string($var:) , "")' is an invalid XPath expression.
'userCSharp:LogicalNe(string($var:) , "")' has an invalid qualified name.

ab. Klar, $var: alleine ist ungültig, da fehlt ein Bezeichner. Aber wer ist für das Fehlen desselben verantwortlich ;) Selbst nachdem ich im Table Looping Grid alles wieder zurückgestellt hatte, lief es nicht mehr. Testweise habe ich alle Logical Functoide nach den Table Extractoren entfernt - damit ist der Build wenigstens nicht mehr abgebrochen. Sobald die Logical Functoide wieder eingefügt waren, kam der Fehler aber auch wieder auf...

vollständigen Artikel anzeigen...

Tags: ,

Microsoft BizTalk Server

Biztalk-Mapper XPath-Injection

by Wolfgang Kluge März 23, 2009 20:42

Heute war ich auf der Suche nach einer Möglichkeit, die Existenz mehrerer Unterelemente festzustellen. Mit vielen vereinzelten Existence-Abfragen würde das sicherlich funktionieren, aber es handelt sich um ein weit verzweigtes XML-Dokument. Zudem sollte es an mehreren Stellen mit unterschiedlichen Elementen funktionieren - es damit musste eine andere Möglichkeit gefunden werden, als eine ganze Existence-Batterie oder das Scripting-Funcoid.

Mein erster Versuch galt dem Mapping-Format. Ich hab probiert, die "Link Source"-Eigenschaft des Links direkt zu ändern, aber beim nochmaligen Öffnen des Mappings wird diese Änderung einfach ignoriert...
vollständigen Artikel anzeigen...

Tags: ,

Microsoft BizTalk Server

Powered by BlogEngine.NET 1.6.1.6
Theme by Mads Kristensen | Modified by Mooglegiant and me ;)