Starting some serious development in Microsoft BizTalk 2013 and I was looking for a way to streamline some of our maps. One common place where we have clutter is in the if..then..else design pattern. Typically you want to map one element if something is true, and if it's not true, you want to map something else instead.
BizTalk doesn't provide functoids that do that neatly, and over the years several people have commented on this; it's a problem that goes right back to at least BizTalk 2006.
In this post I'm going to show you the custom functoids that I created to try to streamline my maps.
In this made-up example I present an input and output schema and a typical map:
In the output message I want a single contact number. If the contact prefers to be called in the day, we want to use the daytime number, otherwise the evening number.
In the map there is a Logical Equal functoid which compares the PreferredContactTime element with the string constant 'Daytime' this is fed into a Logical NOT functoid. These two feed into Value Mapping functoids which will only map their second input to the output if the logical input is true. In this map one and only one of the Value Mappers will operate because their logical inputs are mutually inverted.
This pattern is pretty common, but it requires 4 functoids and a lot of links and things get cluttered quickly. Also in this example, having both Value Mapping functoids pointing to the same output element raises a warning in the map compiler, which isn't smart enough to know that these mappings are mutually exclusive.
I thought it would be easy to fix this, creating a custom If Then Else functoid, but I ran into a significant problem. Looking around for a solution I found that I wasn't the first to try this:
<TL; DR;> The logical functoids do not allow their outputs to be connected to custom functoids.
Having read Part III of Jan Eliasen's article I almost gave up. But as is often the case, a good night's sleep helped an answer coalesce from the morphic fields.
All I had to do was write two custom functoids...
My If Then Else functoid was fine, the problem was that Microsoft saw fit to restrict the connectivity of their built in logical functoids, but the logical category of functoids is not restricted, I could write my own, and determine my own connectivity. The first thing to do was to write a runty little console app, to instantiate the built in functoid I wanted to copy and fish out all the relevant properties.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.BizTalk.BaseFunctoids; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { BaseFunctoid f = new IsNilFunctoid(); Console.WriteLine("-------------"); Console.WriteLine(f.OutputConnectionType.ToString()); Console.WriteLine("-------------"); Console.WriteLine(f.GetScriptBuffer(ScriptType.CSharp, 5)); Console.WriteLine("-------------"); Console.WriteLine(f.GetScriptGlobalBuffer(ScriptType.CSharp)); Console.WriteLine("-------------"); Console.WriteLine(f.GetMinParams(ScriptType.CSharp)); Console.WriteLine("-------------"); Console.WriteLine(f.GetMaxParams(ScriptType.CSharp)); Console.WriteLine("-------------"); Console.WriteLine(f.Category.ToString()); Console.WriteLine("-------------"); Console.WriteLine(f.Description); Console.WriteLine("-------------"); Console.WriteLine(f.Name); Console.WriteLine("-------------"); Console.WriteLine(f.Tooltip); Console.WriteLine("-------------"); int pCount = 0; while (pCount < 5 && pCount < f.GetMaxParams(ScriptType.CSharp)) { try { Console.WriteLine(f.GetInputConnectionType(pCount).ToString()); } catch { pCount = 200; } pCount++; } Console.WriteLine("-------------"); Console.WriteLine(f.HasVariableInputs.ToString()); Console.WriteLine("-------------"); Console.WriteLine(f.RequiredGlobalHelperFunctions.ToString()); Console.WriteLine("-------------"); } } }
Now I know everything I need to know to write a new custom functoid which does the same job... BUT it allows the output to be connected to anything.
using Microsoft.BizTalk.BaseFunctoids; using System; using System.Reflection; namespace CustomFunctoids { public class ExLogicalEq : BaseFunctoid { public ExLogicalEq() : base() { this.ID = (int)FunctoidId.ExLogicalEq; SetupResourceAssembly("CustomFunctoids.Resources", Assembly.GetExecutingAssembly()); SetName("IDS_EXLOGICALEQ_NAME"); SetTooltip("IDS_EXLOGICALEQ_TOOLTIP"); SetDescription("IDS_EXLOGICALEQ_DESCRIPTION"); SetBitmap("IDB_EXLOGICALEQ_BITMAP"); this.Category = FunctoidCategory.Logical; this.SetMinParams(2); this.SetMaxParams(2); AddInputConnectionType(ConnectionType.AllExceptRecord); AddInputConnectionType(ConnectionType.AllExceptRecord); OutputConnectionType = ConnectionType.AllExceptRecord; AddScriptTypeSupport(ScriptType.CSharp); SetScriptBuffer(ScriptType.CSharp, inlineScript); RequiredGlobalHelperFunctions = InlineGlobalHelperFunction.IsNumeric; } string inlineScript = @"public bool ExLogicalEq(string val1, string val2) { bool ret = false; double d1 = 0; double d2 = 0; if (IsNumeric(val1, ref d1) && IsNumeric(val2, ref d2)) { ret = d1 == d2; } else { ret = String.Compare(val1, val2, StringComparison.Ordinal) == 0; } return ret; }"; } }
And this is the code for my If Then Else functoid:
using Microsoft.BizTalk.BaseFunctoids; using System; using System.Reflection; namespace TMS.BizTalk2013.CustomFunctoids { public class AorBMapping : BaseFunctoid { public AorBMapping() : base() { this.ID = (int) FunctoidId.AorBMapping; SetupResourceAssembly("TMS.BizTalk2013.CustomFunctoids.Resources", Assembly.GetExecutingAssembly()); SetName("IDS_AORBMAPPING_NAME"); SetTooltip("IDS_AORBMAPPING_TOOLTIP"); SetDescription("IDS_AORBMAPPING_DESCRIPTION"); SetBitmap("IDB_AORBMAPPING_BITMAP"); this.Category = FunctoidCategory.None; this.SetMinParams(3); this.SetMaxParams(3); AddInputConnectionType(ConnectionType.FunctoidLogical); AddInputConnectionType(ConnectionType.AllExceptRecord); AddInputConnectionType(ConnectionType.AllExceptRecord); OutputConnectionType = ConnectionType.AllExceptRecord; SetScriptBuffer(ScriptType.CSharp, inlineScript); } string inlineScript = @"public string AorBMapping(string val1, string val2, string val3) { return ((val1==""true"")?val2:val3); }"; } }
Now finally my map can look like this.
Ta-daa!
No Comments/Pingbacks for this post yet...
Everything that's happening in the world of carbon14
Mon | Tue | Wed | Thu | Fri | Sat | Sun |
---|---|---|---|---|---|---|
<< < | > >> | |||||
1 | 2 | 3 | 4 | 5 | 6 | |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 |