Solving the "If-Then-Else" problem in a map - part II

by eliasen 18. November 2008 00:01

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:

IfThenElseCustomFunctoid

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()) , &quot;Jan&quot;)" />
    <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

Tags:

Comments (1) -

Joseph Choi
Joseph Choi
11/18/2008 9:38:01 PM #

looking forward to your solution.  i tried to get this particular problem to work in an assembled functoid without success.

Reply

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

About the author

Jan Eliasen is 37 years old, divorced and has 2 sons, Andreas (July 2004) and Emil (July 2006).

Jan has a masters degree in computer science and is currently employed at Logica Denmark as an IT architect.

Jan is a 6 times Microsoft MVP in BizTalk Server (not currently an MVP) and proud co-author of the BizTalk 2010 Unleashed book.

BizTalk Server 2010 Unleashed


Buy from Amazon

Microsoft MVP


6 times: July 2004, July 2008, July 2009, July 2010, July 2011, and July 2012. Not currently an MVP.

MCTS

Image to show

Month List

Page List