Hi all
This is the second post in a series about solving the "If-Then-Else" problem in a map. In my first post I discussed how to use BizTalks built-in functionality to solve the problem. Neither of the three proposed solutions really seem nice to me, so I wondered how difficult it might be to code a custom functoid that does the trick. It turned out to be unexpectedly hard, and this post tries to clarify my findings and explain them.
So, to continue from my previous post, to me the best solution is to code your own functoid, that mimics the value mapping functoid, but adds an "else" part. So basically just a third parameter to the value mapping functoid that is returned in case the first parameter is false.
For information about how to program a custom functoid, please visit http://msdn.microsoft.com/en-us/library/aa560879.aspx - I wont go into details about that here. I will just comment on the issues I have had with creating this particular functoid.
The final solution should give me the possibility to have a map like this one:
YES, I know my icons are very bad... Anyway, three inputs: a boolean and two values.
So, getting to the code, my first try looked like this:
-- BEGIN CODE
this.SetMinParams(3);
this.SetMaxParams(3);
this.Category = FunctoidCategory.ValueMapping;
this.OutputConnectionType = ConnectionType.AllExceptRecord;
AddInputConnectionType(ConnectionType.AllExceptRecord);
AddInputConnectionType(ConnectionType.AllExceptRecord);
AddInputConnectionType(ConnectionType.AllExceptRecord);
-- END CODE
I have removed irrelevant lines, such as setting up the resources, setting the ID, and so on.
First of all, in the code of a custom functoid, you need to specify which category the functoid should belong to. The possibilities are listed at http://msdn.microsoft.com/en-us/library/microsoft.biztalk.basefunctoids.functoidcategory.aspx. I hadn't really looked at this list, since I thought that the intellisense in VS.NET 2005 was good enough. Since my functoid is an advanced value mapping functoid, I chose he value mapping category. This turned out a but different than I thought. It turns out, that the category you assign to a custom functoid not only determines where in the toolbox it should be placed, but also sometimes some extra functionality is added to the functoid. Given my map above, I had expected that the created XSLT would just call my functoid with the three parameters and then my code would do the rest. But the generated XSLT looks like 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 userCSharp" version="1.0" xmlns:s0="http://eliasen.eu.BizTalk.TestProject.InputSchema" xmlns:ns0="http://eliasen.eu.BizTalk.TestProject.OutputSchema" xmlns:userCSharp="http://schemas.microsoft.com/BizTalk/2003/userCSharp">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="/s0:InputRoot" />
</xsl:template>
<xsl:template match="/s0:InputRoot">
<xsl:variable name="var:v1" select="userCSharp:LogicalEq(string(qualifier/text()) , "Jan")" />
<ns0:OutputRoot>
<xsl:if test="string($var:v1)='true'">
<xsl:variable name="var:v2" select="IfTrue/text()" />
<OutputField>
<xsl:value-of select="$var:v2" />
</OutputField>
</xsl:if>
</ns0:OutputRoot>
</xsl:template>
</xsl:stylesheet>
I have removed all the lines associated with the equals-functoid as that included three inline c# methods which are irrelevant. Anyway, as you can see, my functoid is not being called at all. Because I chose the ValueMapping category, the generated xslt assumes it is actually a built-in value mapping functoid and it totally overrules the logic inside the functoid.
So, this really came as a surprise to me... but well...it makes sense when you think about it, naturally. Some of the types of functoids just require logic that goes beyond the code inside a functoid.
So, the ValueMapping category just didn't work for me. Then I thought; "Oh, who cares?"? I will just use the String category instead, because those certainly do not have weird functionality around them... they get input and return a string as output, that is it. And the functoid will just appear in the String group in the toolbox in VS.NET.
That gave me a new headache, that was another surprise; You cannot connect the output of a logical functoid to the input of a string functoid. So... I was stuck.
One of my solutions would accept the output of a logical functoid as input, but my functoid logic was overridden. The other simply wouldn't accept the output of a logical functoid as input.
I have tried the following FunctoidCategories:
Category | Description |
Assert | Terminates when logical functoid returns true. Has the wrong value in output field when logical functoid returns false. |
Conversion | Cannot connect output of logical functoid to input. |
Count | Cannot connect output of logical functoid to input. |
Cumulative | Cannot connect output of logical functoid to input. |
DatabaseExtract | Cannot connect output of logical functoid to input. |
DatabaseLookup | Cannot connect output of logical functoid to input. |
DateTime | Cannot connect output of logical functoid to input. |
ExitenceLooping | Cannot connect output of logical functoid to input. |
Index | Cannot connect output of logical functoid to input. |
Iteration | Cannot connect output of logical functoid to input. |
Keymatch | Cannot compile. You get an "Object not set to an instance of an object" exception. |
Logical | The output field isn't created. |
Looping | Cannot connect output of logical functoid to input. |
MassCopy | Cannot connect output of logical functoid to input. |
Math | Cannot connect output of logical functoid to input. |
NilValue | Only creates output field if logical functoid returns true and then it adds the xsi:nil attribute and no value in the output field. |
Scientific | Cannot connect output of logical functoid to input. |
Scripter | The scripting functoid has no script type set, either external or inline, so proper code cannot be generated for it. |
String | Cannot connect output of logical functoid to input. |
TableExtractor | Cannot connect output of logical functoid to input. |
TableLooping | Needs the table grid configured and is therefore useless. |
Unknown | Cannot connect output of logical functoid to input. |
ValueMapping | Only creates the output field if the logical functoid returns true. |
XPath | Cannot connect output of logical functoid to input. |
So, basically, I haven't been able to do it... for now. Either I cannot connect a logical functoid to my custom functoid, I get an error either at compile time or something goes wrong semantically at runtime.
I haven't given up 100% yet... but I must say, that the task has turned out to be a whole lot more difficult than I thought it would be.
Look out for a part 3 in this series... if it comes, I will have solved this issue :-)
--
eliasen