Assembly call from Logic App XSLT

Posted: May 25, 2022  |  Categories: Azure BizTalk
Tags: msxsl XSLT

In my last blog I said I want use an assembly instead of a BizTalk functoid in a Logic App. For this reason I show how I tried it here. In the first place, I followed John Scott. This blog is a tale of the limitations of this approach in Azure.

Add referenced Assembly to an Integration account

Use a powershell script in your devops like this;

Get-ChildItem "$(System.DefaultWorkingDirectory)/$(edi.templatesPath)/edi-batch-biztalk-assemblies-ia" -Filter *.dll|
Foreach-Object {
    $assemblyName=[io.path]::GetFileNameWithoutExtension($_)
    $assemblyFileName=$_
    Write-Host "Deploying assembly:$assemblyName from file:$assemblyFileName"

    try 
	{
	    #throws an error if the assembly does not exist
		Set-AzIntegrationAccountAssembly -ResourceGroupName "edi-$(edi.env)-rg" -IntegrationAccountName "edi-$(edi.env)-ia" -AssemblyName $assemblyName -AssemblyFilePath "$(System.DefaultWorkingDirectory)/$(edi.templatesPath)/edi-batch-biztalk-assemblies-ia/$assemblyFileName"
	}
	catch 
	{
		New-AzIntegrationAccountAssembly -ResourceGroupName "edi-$(edi.env)-rg" -IntegrationAccountName "edi-$(edi.env)-ia" -AssemblyName $assemblyName -AssemblyFilePath "$(System.DefaultWorkingDirectory)/$(edi.templatesPath)/edi-batch-biztalk-assemblies-ia/$assemblyFileName"
	}
}

As result this adds any assembly in the edi-batch-biztalk-assemblies-ia folder to the integration account.

Adding the BizTalk XSLT to the integration account

Use a powershell script like this in your devops;

Get-ChildItem "$(System.DefaultWorkingDirectory)/$(edi.templatesPath)/edi-batch-biztalk-maps-ia/DEVOnly/" -Filter *.xsl, *.liquid | 
Foreach-Object {
    $mapName=[io.path]::GetFileNameWithoutExtension($_)
    $mapFileName=$_
    Write-Host "Deploying map:$mapName from file:$mapFileName"

    try 
	{
	    #throws an error if the map does not exist
		Set-AzureRmIntegrationAccountMap -ResourceGroupName "edi-$(edi.env)-rg" -Name "edi-$(edi.env)-ia" -MapName $mapName -mapFilePath "$(System.DefaultWorkingDirectory)/$(edi.templatesPath)/edi-batch-biztalk-maps-ia/UATOnly/$mapFileName" -Force
	}
	catch 
	{
		New-AzureRmIntegrationAccountMap -ResourceGroupName "edi-$(edi.env)-rg" -Name "edi-$(edi.env)-ia" -MapName $mapName -mapFilePath "$(System.DefaultWorkingDirectory)/$(edi.templatesPath)/edi-batch-biztalk-maps-ia/UATOnly/$mapFileName"
	}
}

As result this adds the XSLT in the edi-batch-biztalk-maps-ia/UATOnly/ folder to the integration account.

The Logic App XSLT with referenced Assembly

In short the Logic App looks like this;

Testing the Logic App

Firstly, I saw the dreaded error ;

"An error occurred while transforming the given input with the provided map. Details: 'Cannot find a script or an extension object associated with namespace 'http://schemas.microsoft.com/BizTalk/2003/ScriptNS0'.'."

With this in mind adding a msxsl:script element to the XSLT like this was the next step;
	<msxsl:script language="C#" implements-prefix="ScriptNS0">
		<msxsl:assembly name="BidOne.Common.Helpers.XRef.UOM, Version=1.0.0.0, Culture=neutral, PublicKeyToken=069877e015803704" />
		<msxsl:using namespace="BidOne.Common.Helpers.XRef.UOM" />
		<![CDATA[        public static string GetUOM(string UOMURL, string key, string OcpApimSubscriptionKey)
        {
            // maxRequestTries is a private class member set to 3 by default, 
            // optionally set via a constructor parameter (not shown)
            var remainingTries = maxRequestTries;
            var finalException = "";

            do
            {
                try
                {
                    remainingTries = remainingTries - 1;
                    string data = AsyncGetUOM(UOMURL, key, OcpApimSubscriptionKey).GetAwaiter().GetResult();
                    return data;
                }
                catch (Exception e)
                {
                    finalException = e.Message;
                }
            }
            while (remainingTries > 0);

            return "UOM not found: " + finalException;

        }
    ]]>
		<![CDATA[        public static async Task<string> AsyncGetUOM(string UOMURL, string key, string OcpApimSubscriptionKey)
        {

            using (var request = new HttpRequestMessage(HttpMethod.Get, UOMURL))
            {
                request.Headers.Add(key, OcpApimSubscriptionKey);

                try
                {
                    var response = await HttpClient.SendAsync(request);
                    if (response.IsSuccessStatusCode)
                    {
                        //return response.Content.ReadAsStringAsync().Result;
                        response.EnsureSuccessStatusCode();
                        return await response.Content.ReadAsStringAsync();
                    }
                    if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                    {
                        throw new Exception("No valid API key provided.");
                    }
                    if (response.StatusCode >= System.Net.HttpStatusCode.InternalServerError)
                    {
                        throw new Exception("There is a problem with the UOMMapping service");
                    }
                }
                catch (TaskCanceledException taskException)
                {
                    if (!taskException.CancellationToken.IsCancellationReques‌​ted)
                    {
                        throw new Exception("Timeout expired trying to reach the UOMmapping service.");
                    }
                    throw taskException;
                }
                catch (HttpRequestException httpException)
                {
                    throw httpException;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                return "UOM not found";
            }
        }
    ]]>
	</msxsl:script>

Adding the msxsl:script element to the generated XSL will help Logic Apps associate the script with the assembly. Redeploying and testing gave a different error.

BadRequest. The 'Xslt' action failed due to incompatible map 'X_SelectOutputcXMLConfig_IF_Invoice2IF_Invoice'. Error details: 'An error occurred while processing map. 'XSLT compile error.''.

Adding the following msxsl:using

   <msxsl:using namespace="System.Net.Http" />
   <msxsl:using namespace="System.Threading.Tasks" />

gave the following error.

BadRequest. The 'Xslt' action failed due to incompatible map 'X_SelectOutputcXMLConfig_IF_Invoice2IF_Invoice'. Error details: 'Error(s) occured while compiling the Xslt. Error Details: error CS0234: The type or namespace name 'Http' does not exist in the namespace 'System.Net' (are you missing an assembly reference?)'.

Now we are busted. It seems we are not able to use the System.Net.Http namespace.

Conclusion

This highlights an important difference between the Azure implementation calling an assembly in XSLT and that in BizTalk. BizTalk uses XSLT extension objects whereas Azure uses msxsl scripting. As a consequence it seems that the version of msxsl does not use a .Net framework that uses more modern C# implementations such System.Net.Http. Script blocks are supported only in.NET Framework. They are not supported on.NET Core or.NET 5.0 or later. Where to from here? I am not sure I am going to sleep on it and comeback.

turbo360

Back to Top