Total order weight calculation UI customization performance input

Hello,

I am in the performance testing stage of my customization. It basically looks at all of the lines in the order and sums up the total order weight. It also does a volume calculation as well. I’m using the part adapter get by ID (taking the part num from the order line). Performance is okay, but I’m thinking it may be improved. On orders with large quantites or more than 10 lines it can take 1 minute to calculate.

Does the community approve, or can the code be tweaked to be more efficient? Thanks!

private void calcOrderWeightAndPalletCount()
{
int palletVolume = 90;
decimal totalOrderVolume = 0;
decimal totalOrderWeight = 0;
var currentOrderView = oTrans.Factory(“OrderDtl”); // Get the current UI view
decimal lineOrderQty;
decimal totalLineweight;
decimal totalLineVolume;
decimal partNetWeight;
decimal partNetVolume;
string currentLinePart; //Get the selected part so we can pull part weight info from the adapter
PartAdapter partAdapter = new PartAdapter(oTrans); //Create the part adapter so we have access to part database info
DataRow partAdapterRow;
partAdapter.BOConnect(); // connect to our part adapter

        if (currentOrderView.dataView.Count == 0)
        {
            MessageBox.Show("No Order Line(s)");
        }
        else
        {
            var currentOrderRow = currentOrderView.dataView[currentOrderView.Row]; // Get the currently selected record/row from the ui view
            for (int i = 0; i < currentOrderView.dataView.Count; i++)
            {
                currentOrderRow = currentOrderView.dataView[i];// Look at the current order row
                currentLinePart = currentOrderRow["PartNum"].ToString();// Grab the part num from the current row
                lineOrderQty = (decimal)currentOrderRow["SellingQuantity"];// Grab the order quantity from the current row
                if (lineOrderQty != 0)
                {
                    if (partAdapter.GetByID(currentLinePart)) //Ensure we have a part
                    {
                        partAdapterRow = partAdapter.PartData.Part.Rows[i]; //Create the part data row for us to work with

                        partNetWeight = (decimal)partAdapterRow["NetWeight"]; //Pull the weight information from the row
                        partNetVolume = (decimal)partAdapterRow["NetVolume"]; //Pull the volume information from the row

                        totalLineweight = lineOrderQty * partNetWeight; //Calculate the total weight of the line
                        totalOrderWeight += totalLineweight; //Running total for the order
                        totalLineVolume = lineOrderQty * partNetVolume; //Calculate the total volume of the line
                        totalOrderVolume += totalLineVolume; //Running total for the order
                    }
                }
            }

            txtTotalOrderWeight.Text = totalOrderWeight.ToString();//Display in our UI
            finalPalletCount = (int)Math.Ceiling((totalOrderVolume / palletVolume));//Run the formula for pallet calculation

            txtPalletCount.Text = finalPalletCount.ToString();//Display in our UI
            if (totalOrderWeight > 0)
            {
                txtTotalOrderWeight.BackColor = Color.LightGreen; //Highlight the change
            }
            if (finalPalletCount > 0)
            {
                txtPalletCount.BackColor = Color.LightGreen; //Highlight the change
            }
        }

        partAdapter.Dispose(); //Cleanup        
    }

Why are you creating a row in your part adapter?

Looks good to me. If you wanted to shave a little bit of time you could scrap the PartAdapter and it’s GetByID and instead use a BOReader.

I think I’d grab all of the pn’s and slap em in a whereclause and return only the fields i actually needed (looks like net wt and volume)

then look at that ds for pn info

pseudo might be like this:

var pns = currentOrderView.dataView.Where(o => o.SellingQty > 0).Select(o => o.PartNum);
string whereC = "PartNum in (parts)"; //obviously you'd have to build this
var myPartDatas = BOReader.GetRows(whereC,"NetWeight, Volume"); //cant recall the actual sig here

foreach(currenOrderView)
{
 //  your old logic - except instead of GetByID, find your data in myPartDatas

}

@Aaron_Moreng he’s not, just referencing it

1 Like

yep, should read closer :slight_smile:

I wondered the same thing on first glance :smiley:

How many times does this method get called? If it’s more than once, you might consider filling out a data structure with the data needed from the part adapter upon load (or some other basic event) and then performing all calculations against the custom data structure.

Thanks for the feedback guys!

This method is called once on a button click event. The part adapter getbyID method is called on every line of an order during method execution to look up the part info from the line.

I’ll play around with staging the data in a ds first and getting it out of there during method execution. I’ll report back on the performance!

This is taking me back to my data structures university course :wink:

2 Likes

The idea is you get rid of all of the overhead of each call PLUS you return a smaller packet. Win - Win although I cant guess at how much it would (if any) save you. If you try it, please let us know how it performs!

Ouch and Double Ouch!! Just because you can, doesn’t mean you should.

GetByID is generally the heaviest weight call you can make on any BO and while the BO Reader is a better option, it is still calling the same Method on the Server (GetByID is a wrapper over GetRows) so the Server processing weight is the same. That said, with BO Reader you do save a lot on the wire traffic and associated processing.

I think I would try and do this using a BAQ and even if you are not able to Sum the data in the BAQ (you should be able to), the resulting table will be much quicker to work with and your AppServer and SQL Server will thank you…

4 Likes

Thanks Rich, Ill try out a dynamic query adapter call using a BAQ and compare the performance! Everyone stay tuned…

Update:

I switched the part look up from part adapter get by ID to a dynamic query adapter using a baq that pulls the info from the part table that takes the current line partnum as a parameter.

WOW! The increase in performance of the calculation is incredible!

With the Part adapter GetByID method my calculation averaged a 22.7 Second exec time. With the Dynamic Query Adapter using a BAQ to grab the same info the calculation averaged 0.589 Second exec time.

The only downside is having to manage a BAQ with a method but with good documentation and organization that shouldn’t be a problem. Id rather do that than have my users wait 1 min on a calculation on each order throughout the day.

Thanks everyone!

Keep the combo Dynamic query adapter and accompanying BAQ as a tool in your toolbox!

1 Like

Can anyone tell me why GetByID is so expensive?

What is it doing behind the scenes?

Get rows is expensive, think of a sql statement like select * from part vs select partnum from part

So, is the get by ID adapter actually grabbing the entire part table and loading it into memory?

Not exactly, rich would be able to explain that better than myself. But getbyid returns all the related data to the part record, which is extensive as you’ve seen, when in reality you need 2 or 3 fields.

I wonder if it’s a get rows with a where clause…

Ahhhh, that would make sense. There is a lot of data to work with when looking at that method in the BL tester results.

1 Like

@Aaron_Moreng - Correct: GetByID is a “convenience” Method that Wraps GetRows and just passes the Where Clause with a single ID. GetByIDs / GetBySysRowID(s) are also GetRows wrappers.

The BOReader is a special case of a GetList / GetRows wrapper in that it calls GetList or GetRows with the appropriate Where Clause but then discards all the data returned from the underlying method call except for the columns specified as return columns. The Servers (App and SQL) still do all the work but the network usage and associated processing are greatly reduced.

GetRows - whether it is returning a single row or all rows - returns the entire data structure associated with a business “entity” - Part, Customer, Quote, Order, Job, etc. The entities just listed are all Heavy Weight and are made up of numerous related tables (parent, child, grandchild,…) and can also include independent but associated tables.

If you need the entire business entity, GetByID is fine but if you only need a few pieces of data, use a BAQ. In cases where the data that you want is logically modified by a Post Processing BPM and you are unable to emulate that logic in a BAQ, use the BOReader.

3 Likes