Widget vs Code Analysis and a little computer science

By no means am I meaning to pick on @jkane but looking at his rather impressive widget usage here lead to me finally doing an analysis I’ve wanted to do for a long time regarding widgets vs code.

There have been a lot of discussions around here about whether widgets should be used and how often, when etc. so I decided to take a quick look for my own amusement.

I attempted to re-create a large widget Function and then do the same exact function without widgets. As examples goes it isn’t a great one, but it serves to illustrate the problem we are trying to solve is simple

  1. Retrieve a record from ABC Code using GetByID
  2. Assign 37 fields into this ABC Code record.

Widget:

Code It Generated 900+ LOC:
using Epicor.Customization.Bpm;
using Epicor.Data;
using Epicor.Hosting;
using Epicor.Utilities;
using Erp;
using Erp.Tables;
using Ice;
using Ice.Contracts;
using Ice.ExtendedData;
using Ice.Tables;
using Ice.Tablesets;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;

namespace EFx.ButWhy.Implementation
{
    partial class WidgetImpl
    {
        protected override int RunStep(int workflowStep)
        {
            var conditionBlockValue = false;

            switch (workflowStep)
            {
                case Epicor.Functions.PredefinedStep.Start: // Invoke BO Method 0
                    try
                    {
                        this.A001_InvokeBOMethodAction2();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 1;
                case 1: // Set Field 1
                    try
                    {
                        this.A002_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 2;
                case 2: // Set Field 2
                    try
                    {
                        this.A003_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 3;
                case 3: // Set Field 3
                    try
                    {
                        this.A004_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 4;
                case 4: // Set Field 4
                    try
                    {
                        this.A005_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 5;
                case 5: // Set Field 5
                    try
                    {
                        this.A006_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 6;
                case 6: // Set Field 6
                    try
                    {
                        this.A007_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 7;
                case 7: // Set Field 7
                    try
                    {
                        this.A008_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 8;
                case 8: // Set Field 8
                    try
                    {
                        this.A009_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 9;
                case 9: // Set Field 9
                    try
                    {
                        this.A010_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 10;
                case 10: // Set Field 10
                    try
                    {
                        this.A011_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 11;
                case 11: // Set Field 11
                    try
                    {
                        this.A012_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 12;
                case 12: // Set Field 12
                    try
                    {
                        this.A013_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 13;
                case 13: // Set Field 13
                    try
                    {
                        this.A014_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 14;
                case 14: // Set Field 14
                    try
                    {
                        this.A015_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 15;
                case 15: // Set Field 15
                    try
                    {
                        this.A016_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 16;
                case 16: // Set Field 16
                    try
                    {
                        this.A017_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 17;
                case 17: // Set Field 17
                    try
                    {
                        this.A018_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 18;
                case 18: // Set Field 18
                    try
                    {
                        this.A019_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 19;
                case 19: // Set Field 19
                    try
                    {
                        this.A020_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 20;
                case 20: // Set Field 20
                    try
                    {
                        this.A021_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 21;
                case 21: // Set Field 21
                    try
                    {
                        this.A022_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 22;
                case 22: // Set Field 22
                    try
                    {
                        this.A023_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 23;
                case 23: // Set Field 23
                    try
                    {
                        this.A024_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 24;
                case 24: // Set Field 24
                    try
                    {
                        this.A025_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 25;
                case 25: // Set Field 37
                    try
                    {
                        this.A026_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 26;
                case 26: // Set Field 38
                    try
                    {
                        this.A027_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 27;
                case 27: // Set Field 39
                    try
                    {
                        this.A028_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 28;
                case 28: // Set Field 40
                    try
                    {
                        this.A029_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 29;
                case 29: // Set Field 41
                    try
                    {
                        this.A030_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 30;
                case 30: // Set Field 42
                    try
                    {
                        this.A031_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 31;
                case 31: // Set Field 43
                    try
                    {
                        this.A032_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 32;
                case 32: // Set Field 44
                    try
                    {
                        this.A033_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 33;
                case 33: // Set Field 45
                    try
                    {
                        this.A034_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 34;
                case 34: // Set Field 46
                    try
                    {
                        this.A035_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 35;
                case 35: // Set Field 47
                    try
                    {
                        this.A036_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 36;
                case 36: // Set Field 48
                    try
                    {
                        this.A037_SetFieldAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return Epicor.Functions.PredefinedStep.Exit;
                default:
                    return Epicor.Functions.PredefinedStep.Unknown;
            }
        }

        private void A001_InvokeBOMethodAction2()
        {
            this.CallService<Erp.Contracts.ABCCodeSvcContract>(
                bo =>
                {
                    System.String _arg01 = this.A001_InvokeBOMethodAction2_arg01();
        
                    ds = bo.GetByID(
                        _arg01);
                });
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A001_InvokeBOMethodAction2_arg01()
        {
            return ("A");
        }

        private void A002_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A002_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A002_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A003_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A003_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A003_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A004_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A004_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A004_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A005_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A005_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A005_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A006_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A006_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A006_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A007_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A007_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A007_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A008_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A008_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A008_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A009_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A009_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A009_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A010_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A010_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A010_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A011_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A011_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A011_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A012_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A012_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A012_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A013_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A013_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A013_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A014_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A014_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A014_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A015_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A015_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A015_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A016_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A016_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A016_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A017_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A017_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A017_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A018_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A018_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A018_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A019_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A019_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A019_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A020_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A020_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A020_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A021_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A021_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A021_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A022_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A022_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A022_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A023_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A023_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A023_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A024_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A024_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A024_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A025_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A025_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A025_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A026_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A026_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A026_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A027_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A027_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A027_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A028_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A028_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A028_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A029_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A029_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A029_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A030_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A030_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A030_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A031_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A031_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A031_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A032_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A032_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A032_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A033_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A033_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A033_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A034_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A034_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A034_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A035_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A035_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A035_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A036_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A036_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A036_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }

        private void A037_SetFieldAction()
        {
            var query = ds.ABCCode.Where(row => !row.Unchanged());
        
            foreach (var item in query)
            {
                item.ABCCode = this.A037_SetFieldAction_ABCCode(item);
            }
        }
        
        [Epicor.Customization.CustomCode]
        private System.String A037_SetFieldAction_ABCCode(Erp.Tablesets.ABCCodeRow item)
        {
            return ("A");
        }
    }
}

Non Widget Method:

Code It Generated 118 LOC:
using Epicor.Customization.Bpm;
using Epicor.Data;
using Epicor.Hosting;
using Epicor.Utilities;
using Erp;
using Erp.Tables;
using Ice;
using Ice.Contracts;
using Ice.ExtendedData;
using Ice.Tables;
using Ice.Tablesets;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;

namespace EFx.ButWhy.Implementation
{
    partial class WidgetFunctionImpl
    {
        protected override int RunStep(int workflowStep)
        {
            var conditionBlockValue = false;

            switch (workflowStep)
            {
                case Epicor.Functions.PredefinedStep.Start: // Invoke BO Method 0
                    try
                    {
                        this.A001_InvokeBOMethodAction2();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return 1;
                case 1: // Execute Custom Code 0
                    try
                    {
                        this.A002_CustomCodeAction();
                    }
                    catch (Ice.Common.BusinessObjectException ex)
                    {
                        this.RememberException(ex);
                    }
                    return Epicor.Functions.PredefinedStep.Exit;
                default:
                    return Epicor.Functions.PredefinedStep.Unknown;
            }
        }

        private void A001_InvokeBOMethodAction2()
        {
            this.CallService<Erp.Contracts.ABCCodeSvcContract>(
                bo =>
                {
                    System.String _arg01 = this.A001_InvokeBOMethodAction2_arg01();

                    ds = bo.GetByID(
                        _arg01);
                });
        }

        [Epicor.Customization.CustomCode]
        private System.String A001_InvokeBOMethodAction2_arg01()
        {
            return ("A");
        }

        [Epicor.Customization.CustomCode]
        private void A002_CustomCodeAction()
        {
            foreach (var x in this.ds.ABCCode.Where(row => !row.Unchanged()))
            {
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
                x.ABCCode = "A";
            }
        }
    }
}

Neither of these options is particularly elegant, but here’s a breakdown of their Big O efficiency/complexity metrics along with other relevant considerations. Big O complexity is a way to describe how the performance or scalability of an algorithm changes with input size.

Big O Categories:

  • O(1) – Constant Time:
    This means the algorithm’s performance does not depend on the size of the input—it runs in the same amount of time regardless. For example, a function like GetDateTime() always returns the current date and time in constant time, whether today, tomorrow, or next year.

  • O(n) – Linear Time:
    Performance scales directly with input size. The larger the input, the longer the algorithm takes. For instance, a counting loop:
    CountTo(10); // Takes less time
    CountTo(100); // Takes 10x longer

  • O(n²) – Quadratic Time:
    Here, performance scales exponentially with input size. For example, a standard sorting algorithm might iterate through a list multiple times, resulting in a time complexity proportional to the square of the input size. Doubling the input roughly quadruples the work.

Aspect Code Widget
Number of Dataset Iterations 1 (single pass through ds.ABCCode) (m) (one pass per field, (m = 36) in this case)
Number of Assignments (n * m) (all fields assigned in one loop) (n * m) (one assignment per iteration per field)
Number of Methods Invoked 2: A001_InvokeBOMethodAction2, A002_CustomCodeAction (2 + m): A001_InvokeBOMethodAction2 + (m) field-specific methods
Code Jump Points Minimal: 1 return per switch case High: (m) return points, one for each field-specific case
Workflow Complexity Simple: Linear execution through 2 steps High: Linear execution but with (m) additional workflow steps
Big-O Complexity (Dataset) (O(n)) (O(n * m))
Big-O Complexity (Workflow) (O(1)): Workflow has minimal state transitions (O(m)): Workflow has (m) transitions
Overhead per Field Update Minimal (loop runs once, condition per field) High (new loop and query per field update)
Code Reusability Moderate: Harder to reuse logic across fields High: Field-specific logic encapsulated in methods
Code Maintainability Low: Single large method harder to debug High: Modular field-specific logic improves clarity
Readability Moderate: All logic in one place but harder to trace specific fields High: Clear separation of logic per field
Testing Simplicity Low: Harder to isolate and test individual fields High: Easy to test/update individual field logic

Anyway I’m not necessarily arguing one way or another and I don’t want to get into another religious war It was mostly morbid curiosity. But here are some of my own observations.

  • Code First processes the dataset in a single pass, reducing iteration overhead, while Widget First iterates once for each field update, increasing execution time with each individual widget at a rate of m*n (once per widget)
  • Widget First is more modular, with each field update encapsulated in its own method, making it easier to debug or extend compared to the centralized logic in Code First. However this is abstracted to us by the software
  • Code First minimizes the number of function calls, while Widget First has a higher number of method invocations due to its field-specific design and a billion return points leading to some nice spaghetti code (which is admittedly abstracted by the software)
  • Code First scales better with an increasing number of rows in the dataset
  • Workflow complexity is minimal in Code First with fewer jump points, whereas Widget First has higher complexity due to many workflow steps and returns.
  • Code First provides significantly better performance but you sacrifice the ease of maintainability by having to write code.
  • Widget First Generated over 900+ LOC more isn’t always better
  • Widget First 118ish+ LOC Less isn’t always better, more compact code generally means more inherit complexity in a single method.

I guess what I am trying to illustrated is that while yes Widgets can be convenient you are sacrificing efficiency in some cases. Similarly by writing a bunch of code you may be making a more efficient BPM or Function but you are sacrificing maintainability. So Right tool for the Right Job.

As Confucius said “Do not use a cannon to kill a mosquito.”

13 Likes

Great review, thanks for doing that for us to learn!

1 Like

Yes Yes Yes Agree GIF

This is the exact reason I am so active on this site @josecgomez !! I would not get this information anywhere else! I am proud to have triggered this post.

Jim Carrey Bow GIF

5 Likes

How are you sacrificing maintainability? If a method that a widget is using changes, you cannot simply make the change on the widget. You have to delete the widget and start over. This is actually worse than code, because you can simply adjust the parameters to the call. I never understood the “Widgets upgrade better” argument, because the system doesn’t add in or change method calls. If you’re code is using the same BO’s, it’s the same number of “breaks” but one is trivial to fix, the other you have to start over.

4 Likes

I am arguing the point that Epicor touts that “Widgets upgrade cleanly.” Ceteris paribus applies here, I can’t defend their buggy a** widgets one way or another. For this analysis I am assuming in a perfect world every widget upgrades cleanly. Trying to keep things Apples to Apples.

1 Like

I first saw that gif as raising a different finger :rofl:

4 Likes

Great analysis. A few of questions:

And I wonder how the code generation would look if the multiple assignment idea made it?

How do the more declarative Update By Query or Fill By Query widgets compare to a Custom Code block where most of our solutions are iterative by nature?

2 Likes

I think that was the intention of that gif’s designer :joy::joy: and maybe @jkane’s :face_with_raised_eyebrow:

2 Likes

Did you get an actual time comparison to complete the transaction?

That would (I assume) get it much closer to the Code First (in this example)

No, this is theoretical time

I don’t have the time nor the crayons to try and time this, it would require us to do significantly more complex stuff the time difference above between O(n) and O(n*m) is micro seconds tantamount to one or two more clock cycles @ 2.XX Ghz /s

2 Likes

Well, in that case, Do I have an investment for you!

2 Likes

I’ve always seen this as the items (DataSets, Fields, BAQs, etc.) you are working on are knowable since they exist in fields. With custom code, you have to do regex to find them. I have a BAQ that can find who is on the To: Cc: and Bcc: for widgets but I can’t do the same thing easily with custom code.

1 Like

Well, no. This is why Jose brought up Big(O) notation. Context matters. For small sets, the time difference might not matter, but matter a lot more for larger sets.

1 Like

I would argue that’s creation, not maintainability. But the traces give you pretty much all you need anyways, not to mention the auto complete.

Yes, widgets might help you get started if you don’t know what you’re doing. But once you understand how things are generally structured, they become tedious quite quickly.

1 Like

No, it was the first one that popped up on the search and I liked the pizazz it delivered.

2 Likes

I would argue the opposite. DataSets change, methods change and we need that information at maintainability/testing time, not creation.

But the widgets don’t update themselves, and if it’s broken, Epicor doesn’t give you a way to fix it. You have to recreate the widget from scratch.

What widget are you referencing? The ones I used I can easily go in and change.

But I can find where they are broken without searching through random code blocks. Unless of course, we had source control…

Pondering Anthony Anderson GIF by BET

3 Likes