Showing posts with label kernel. Show all posts
Showing posts with label kernel. Show all posts

Friday, March 9, 2012

Increase Performance by Removing Unused Overridden Methods?

It's been in the back of my mind for quite awhile now to figure out how the kernel of Dynamics AX actually handles it's delete_from and update_recordset logic. Sometimes, the kernel decides to emit exactly one DELETE FROM query to the database versus one 'DELETE FROM table WHERE RecId = @P1' per record in the table buffer.

I found a case on PriceDiscTable (this situation is not limited to just this object), a noticeably large table especially for our business, where I noticed multiple DELETE FROM statements getting emitted to the database, taking a very long time to complete a daily task our business runs. I looked at the table and noticed that the delete() method on the table was indeed overridden, but upon investigating the details of the method, only super() was getting called inside (along with some commented out code that we used in the past and plan to use in the future, but that's beside the point).

I removed the method as there really is no functional difference in just leaving it hidden (even though I know the reason why we wanted the commented code there, we didn't want to lose it). Lo and behold, our daily task now emits one single DELETE FROM with the criteria we specified in the WHERE clause.

One would think that the kernel would notice and see that the compiled version of the overridden delete() method with just the super() call is identical to the default "hidden" delete() method and handle them the same. But, we've been duped again. We'll have to store the commented out code we had in there somewhere else even though it would've been much more convenient to just keep in where it was.
As a side note, having at least one Delete Action on your table object will also force the kernel to emit multiple DELETE FROM queries. Removing them will tell the kernel that it doesn't have to perform a row-by-row operation and can emit the DELETE FROM in one statement.

And as (Joris de Gruyter of Dynamics AX Musings) has graciously pointed out in a comment, having database logging or alerts enabled on your table and/or having any MEMO fields on that table will also force a multiple DELETE FROM, who knew?

Thursday, February 9, 2012

Kernel Function Madness: any2str()

I'm a developer.  I'd treat this function as any would.  Here we have a function called any2str and takes in an anytype object and returns a string representation.  That's great because I have a dynamically changing anytype object that could be an integer one time and a date the next.  I also need to get a string representation of each type to I can put the values into the same column of a table to report on later.  So like any developer would do you code up your logic and write:
table.stringField = any2str(dynamicObject);
Sweet.  Everything compiles, let's continue writing code.
...
***Days pass***
...
Ok, time to run a test.  What?  Run-time error?  Method is expecting object of type string?  Wait, what line is it talking about?

For a moment I refused to believe that someone wouldn't have been so stupid to code up this method to expect only strings to be passed in.  And even after I realized that was the truth, I still couldn't believe it.  Yes, the method does in fact take in an anytype.  However, the method only works if that anytype is of type string.  So, instead of using the intuitive knowledge us developers have been taught to use, we have to now second guess every kernel function we call into no matter how we much "know" (think) how a method should work.  We've been duped, we've been given a useless str2str() method.

The workaround is below.  Use this as a local function (or as a method in the Global class to make a global method) inside the methods you often would use any2str(), that is, before understanding what the method really does.
str theRealAny2str(anytype _val)
{
    switch (typeof(_val))
    {
        case Types::Date:
            return date2str(any2date(_val), 213, 2, -1, 2, -1, 2);
        case Types::Enum:
            return enum2str(any2enum(_val));
        case Types::Guid:
            return guid2str(any2guid(_val));
        case Types::Int64:
            return int642str(any2int64(_val));
        case Types::Integer:
            return int2str(any2int(_val));
        case Types::Real:
            return num2str(any2real(_val), 1, 2, 1, 0);
    }
    return any2str(_val);
}