Overview
Microsoft BizTalk Server is a rich and robust integration platform and is nearing its twentieth birthday(!). Hundreds, if not thousands, of customers have made significant investments over the years developing their integration solutions on BizTalk Server. As they move workloads to Azure to either run both in conjunction with BizTalk Server and within Azure natively, they would like to be able to leverage those investments with minimal refactoring.
Azure Logic Apps is an integration cloud service which allows users to schedule, automate, and orchestrate tasks, business processes, and workflows. Logic Apps functionality both overlaps and complements functionality in BizTalk, but quite helpfully, it also allows for the reuse of existing BizTalk artifacts, such as schemas and maps.
BizTalk maps transform messages between different schemas, and use a number of tools within the map to manipulate data during the transformation. The maps can also utilize external .NET assemblies for these transformations, but because of how BizTalk references these assemblies natively, the maps need some changes in order to be able to reference those assemblies when run in Logic Apps. This article shows how to edit a map created in BizTalk to utilize external assemblies when run in Logic Apps.
Scenario
For the purpose of this blog, we will setup a small solution consisting of two schemas, a BizTalk map, as well as an external .NET assembly. The full solution of BizTalk artifacts and sample files is available here.
In this scenario, we have two sample schemas: Foo, representing our source schema, and Bar, representing the target.
We also have a .NET Framework class library, called MapperAssemblies, which contains a small class with two trivial methods: AddTodayToValue, which will simply append today's day of the week value to the input value, and ConcatValues, which concatenates the input values.
using System;
namespace MapperAssemblies
{
public class TransformValues
{
public string AddTodayToValue(string input)
{
return $"{input} today is {DateTime.Now.DayOfWeek.ToString()}";
}
public string ConcatValues(string val1, string val2)
{
return string.Concat(val1, val2);
}
}
}
As per most libraries used in BizTalk, this assembly has been signed and added to the global assembly cache (GAC).
Finally, we have the map: This map performs a simple transform of the incoming two values from Foo to the three values in Bar.
Each of the three functoids pictured is referencing the sample assembly code, for exmple:
Testing the map with the following sample input
<ns0:Foo xmlns:ns0="http://MapWithAssmeblyReference.FooSchema">
<Val1>Value 1</Val1>
<Val2>Value 2</Val2>
</ns0:Foo>
Will yield the following output
<ns0:Bar xmlns:ns0="http://MapWithAssmeblyReference.BarSchema">
<Val1>Value 1 today is Monday</Val1>
<Val2>Value 2 today is Monday</Val2>
<Val1Val2>Value 1Value 2</Val1Val2>
</ns0:Bar>
Adding the artifacts to Logic Apps
In order to utilize the BizTalk artifacts in Logic Apps, the artifacts must be stored to an Integration Account in Azure. Integration Accounts are a companion resource to Logic Apps in Azure, and can be used as a container to store integration artifacts such as maps, schemas and assemblies, as well as certificates and trading partner agreements.
Create the Integration Account
- Information on creating an Integration Account is documented here. For the purpose of this sample, the free tier for the Integration Account is adequate.
Adding Schemas
For both the Foo and Bar schemas, perform the following steps:
- Open the Integration Account
- Click on Schemas under Settings
- Click on + Add
- Select "Small File" and click the file icon and navigate to the XSD in your BizTalk solution. The schema name will auto-populate.
- Click "OK" to add the schema
Adding the map
This step is not quite as intuitive as adding the schemas because BizTalk maps files (btm) are not natively supported by Logic Apps. However, as BizTalk files compile to XSLT, the output XSLT can be loaded to the Integration Account.
- Open Visual Studio and open your BizTalk map file.
- Right-click the BizTalk map file in Solution Explorer, and select "Validate Map"
- In the Output window for BizTalk output, there will be a link to the output XSLT. Copy this file location.
- In the Azure portal, open the Integration Account
- Click on Maps under Settings
- Click on + Add
- Leave the Map Type as XSLT
- Click the file icon and navigate to the XSLT path from the "Validate Map" step about. The map name will auto-populate.
- Click "OK" to add the map
Adding the Assembly
- In the Azure portal, open the Integration Account
- Click on Assemblies under Settings
- Click on + Add
- Click the file icon and navigate to the DLL for your assembly. The assembly name will auto-populate.
- Note: For DLLs larger than 2 MB, select the "Large File" option. This will require you to first upload your DLL to Azure Storage and provide the URI to the file.
- Click "OK" to add the assembly
Adding the Logic App
While there are many ways to trigger and test the logic app, in this scenario we will create a simple Logic App with an HTTP Trigger and use Postman to test the Logic App.
Create the Logic App
- In the Azure portal, click "+ Create a resource" and enter Logic App into the search. Select "Logic App" and follow the wizard to create a new Logic App.
- Navigate to the newly created Logic App and select Workflow settings under Settings
- Under "Integration account" select the integration account created in the initial step. Click Save to save the changes.
- Click Logic app designer under Development Tools
- This test will create a simple Logic App with four (4) steps:
- Set the initial trigger to "HTTP request". Do not change any settings on this step.
- Add a XML Validation step to the Logic App after the HTTP request trigger. The Content will be the value of the Body from the HTTP request. Add the "Foo schema for the schema name.
- Add a Transform XML step to the Logic App after the XML Validation step. The Content will be the value of the Body from the HTTP request and the map will be the Foo-2-Bar map imported to the Integration Account.
- Add a Response step to the Logic App after the Transform XML step. The body will be the Transformed XML from the Transform step.
- Save the Logic App
Testing the Logic App
This scenario will use Postman to test the Logic App, but any equivalent tool can be used.
- After saving the Logic App, the HTTP POST URL will be displayed. Copy this URL to Postman and set the request type to "POST"
- In the request body, use the sample used previously to test the map
<ns0:Foo xmlns:ns0="http://MapWithAssmeblyReference.FooSchema">
<Val1>Value 1</Val1>
<Val2>Value 2</Val2>
</ns0:Foo>
- Click "Send" to submit the POST to Logic Apps.
- In the Azure portal, click the "Overview" link and view the Runs history.
Looking at the "Runs" history will display an error in the Transform XML step. The specific error will be similar to the following:
"Code": "Invalid XsltContent",
"Message": "An error occured while processing map. 'Cannot find a script or an extension object associated with namespace 'http://schemas.microsoft.com/BizTalk/2003/ScriptNS0'.'"
Understanding the error
Looking inside the XSLT generated by the BizTalk map will show us the source of the error. When BizTalk generates the XSLT, it leaves a namespace reference to the assembly (ScriptNS0), but does not explicitly reference the assembly, which causes Logic Apps to throw the error as it does not know which assembly to associate with this.
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var s0 ScriptNS0" version="1.0" xmlns:ns0="http://MapWithAssmeblyReference.BarSchema" xmlns:s0="http://MapWithAssmeblyReference.FooSchema" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="/s0:Foo" />
</xsl:template>
<xsl:template match="/s0:Foo">
<xsl:variable name="var:v3" select="string(Val1/text())" />
<xsl:variable name="var:v4" select="string(Val2/text())" />
<ns0:Bar>
<xsl:variable name="var:v1" select="ScriptNS0:AddTodayToValue(string(Val1/text()))" />
<Val1>
<xsl:value-of select="$var:v1" />
</Val1>
<xsl:variable name="var:v2" select="ScriptNS0:AddTodayToValue(string(Val2/text()))" />
<Val2>
<xsl:value-of select="$var:v2" />
</Val2>
<xsl:variable name="var:v5" select="ScriptNS0:ConcatValues($var:v3 , $var:v4)" />
<Val1Val2>
<xsl:value-of select="$var:v5" />
</Val1Val2>
</ns0:Bar>
</xsl:template>
</xsl:stylesheet>
Fixing the error
In order to tell Logic Apps how to associate this reference with the uploaded assembly, we need to tweak the XSLT. To do this we will need to know the assembly details, the name of the namespace for the helper class, as well as the class name and method signatures of all referenced methods.
As shown in the sample below, we will add an msxsl:script element to the generated XSL which will help Logic Apps associate the script with the assembly. The key parts to the element are:
- The implements-prefix attribute indicates which xmlns to associate (e.g. ScriptNS0).
- The msxsl:assembly element gives the assembly information, including name, version, Culture and PublicKeyToken.
- msxsl:using namespace attribute indicates which namespace within the assembly is being referenced.
- One CDATA block for each method being referenced in the mapper.
For this example, the modified XSLT will look like this (note the msxsl:script element):
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var s0 ScriptNS0" version="1.0" xmlns:ns0="http://MapWithAssmeblyReference.BarSchema" xmlns:s0="http://MapWithAssmeblyReference.FooSchema" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0">
<msxsl:script language="C#" implements-prefix="ScriptNS0">
<msxsl:assembly name="MapperAssemblies, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c62342917314647d" />
<msxsl:using namespace="MapperAssemblies" />
<![CDATA[public string AddTodayToValue(string input){ TransformValues helper = new TransformValues(); return helper.AddTodayToValue(input); }]]>
<![CDATA[public string ConcatValues(string val1, string val2){ TransformValues helper = new TransformValues(); return helper.ConcatValues(val1, val2); }]]>
</msxsl:script>
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="/s0:Foo" />
</xsl:template>
<xsl:template match="/s0:Foo">
<xsl:variable name="var:v3" select="string(Val1/text())" />
<xsl:variable name="var:v4" select="string(Val2/text())" />
<ns0:Bar>
<xsl:variable name="var:v1" select="ScriptNS0:AddTodayToValue(string(Val1/text()))" />
<Val1>
<xsl:value-of select="$var:v1" />
</Val1>
<xsl:variable name="var:v2" select="ScriptNS0:AddTodayToValue(string(Val2/text()))" />
<Val2>
<xsl:value-of select="$var:v2" />
</Val2>
<xsl:variable name="var:v5" select="ScriptNS0:ConcatValues($var:v3 , $var:v4)" />
<Val1Val2>
<xsl:value-of select="$var:v5" />
</Val1Val2>
</ns0:Bar>
</xsl:template>
</xsl:stylesheet>
Retesting the Logic App
After modifying the XSLT, we need to re-upload the modified file to the Integration Account.
- Open the Integration Account in the Azure portal
- Select "Maps" under Settings
- Click on the map previously uploaded
- Click the "Update" button and select the new XSLT file
Re-run the test in Postman. The Runs history in the Logic App should indicate a successful run, and there should be a response body in Postman with the values which are generated in the C# assembly.
Summary
In this scenario, we looked at how to modify the XSLT generated by the BizTalk mapper to correctly reference an external assembly when the map is run in Logic Apps.