mfp's two cents

...on Dynamics AX Development!

  • mfp's two cents

    Upgrading to Microsoft Dynamics AX 2009 @ Convergence 2008


    Deepak Kumar and myself will be hosting two sessions on the upgrade process at Convergence in Copenhagen later this month. I hope to see you there.

    Code Title Date/Time Room
    AX14 Upgrading to Microsoft Dynamics AX 2009 11/20/2008 10:30AM-11:30AM Hall A1
    AX14R Upgrading to Microsoft Dynamics AX 2009 11/20/2008 3:30PM-4:30PM Aud 15

    This session will take you through the end-to-end flow of upgrading from Microsoft Dynamics AX 3.0 or Microsoft Dynamics AX 4.0 to Microsoft Dynamics AX 2009. We'll overview the code upgrade as well as the data upgrade process, along with recommended best practices. Examples of the improved upgrade documentation, with a focus on the implementation guidelines, will be highlighted, and we'll cover tips on performing the upgrade more efficiently and effectively. You'll also learn the extra upgrade steps needed if you've extended your solution. 

  • mfp's two cents

    Late night discussion on software development #1: Continuous publishing


    “So tell me”, I start, “when was the last time you used a help system?”

    Søren pours himself another glass of red wine and leans back in his armchair. He is silent for a while. “What do you mean when you say help system?”

    I open my mouth, and close it again. It always amazes me, when I realize the depth of such a seemingly naïve question. My immediate response seems too narrow. I have to break out of my standard way of thinking. A help system is not just a part of a larger system that helps you when you are stuck. It is even more than this. What comes out of my mouth is: “A help system is a system that helps you accomplish your tasks.”

    “In that case I use help systems daily”, Søren proclaims.

    “Really? I thought you already knew all the answers.”, I cannot resist the temptation.

    Søren smiles, “You are also a developer, how do you manage to get anything done without a help system?”

    Apparently Søren is into one of his moods where he answers all my questions with a new question. I sip my wine, take a deep breath and start to elaborate. “As a developer my work is quite dependent of the phase my project is in. Let's look at my favorite, the implementation phase. While writing code I never press the F1 button. I have worked with my current code editor for so long, that I’m fully aware of what it can and cannot do for me.”

    Søren says: “So you have turned off IntelliSense?”

    “No way – I don’t even know any developer crazy enough to do that. Actually I don’t even know an code editor that allows you to turn off IntelliSense.”

    “Please go back to your definition of a help system.”

    It dawns on me. Help system are more than just F1 help. It is anything that enables me to do my job. “Ok”, I say, “So what other help systems do you use?” I ask. I’m starting to feel confident I will earn more than a hangover from tonight.

    “Well,” Søren says, “as a developer I often need to lookup API reference information. To do that I use the web constantly.”

    “So you actually find any useful information out there? I’ve tried a few times but given up. Instead I browse objects locally searching for something that will satisfy my need.”

    “That is one of the industry’s dilemmas. Users who have been disappointed by the documentation available in the help system, usually never come back. So why bother writing documentation, if it is not going to be read?“

    “Exactly, if the documentation isn’t there, then there is no point in looking.”

    Søren continues, “The users also suffers here, instead of looking for information where it is obvious they eagerly jump into the haystack looking for the needle. How can this destructive habit be reversed?”

    A simple question, I think to myself. I answer: “Well, if the software providers just wrote the documentation we need, we wouldn’t have the problem.”

    Søren promptly replies: “The question to ask is: How do you know it is not already there?” I feel like I’m being maneuvered into a corner. “When I installed my environment about 2 years ago, I browsed through the reference documentation, and it was primarily empty placeholders.”,  I pause to think about what I just said. “Naturally these placeholders might have been populated in the meantime.“ I make a mental note to  reserve some time on Monday morning to give the online documentation a second chance.

    Søren finishes off his glass of red wine. He looks content.

  • mfp's two cents

    Version control for everyone

    With Dynamics AX 5.0 there is no longer an excuse for not using a source control system when developing in MorphX. The version control integration options in Dynamics AX 5.0 both cater to larger development organizations by seamlessly integrating with Visual Source Safe and Team Foundation Server, and to smaller development teams who cannot afford the additional overhead of these larger system with a new, simple, yet powerful, version control system: MorphX VCS.

    All 3 flavours enable check-in/check-out/undo check-out/change history/change descriptions/quality bar enforcements etc. Using a version control system can dramatically improve the quality, predictability and productivity of your MorphX projects.

    This channel 9 screencast gives a preview of the version control system integration options in the next release of MorphX - the IDE of Dynamics AX. It shows a side-by-side comparison of the integration options with Team Foundation Server, Visual Source Safe, and MorphX VCS. The last half of the screencast gives a demonstration of MorphX VCS.

    Finally I want to wish everyone a "God Jul" (as you say this time of year where I live.)

  • mfp's two cents

    Now available: Dynamics AX 2009 Pre-Release (CTP3) Demonstration Toolkit


    We are happy to announce that the new Microsoft Dynamics AX 2009 Pre-Release (CTP3) Demonstration Toolkit is now available on Partner Source.

    The Dynamics AX 2009 Pre-Release (CTP3) VPC is an unsupported ready to use pre-release of AX 2009. The Virtual PC image of Microsoft Dynamics AX 2009 Pre-Release (CTP3) enables you to demonstrate the features of Microsoft Dynamics AX 2009 using a single PC or laptop computer.

    AX 2009 Pre-Release (CTP3) Demonstration Toolkit Demo Data.

    This version of the Dynamics AX Pre-release Demonstration Toolkit is based on the new Contoso Demo Data Company. The current demo data version will only have base data and no transactions. We are planning to upload a demo data version with transactions shortly.

  • mfp's two cents

    Rapid Configuration Tool for Dynamics AX 2009 is released!


    The new release of Rapid Configuration Tool (RCT) for Dynamics AX2009 is now available for download at Partner Source.

    The Rapid Configuration Tool is integrated into Microsoft Dynamics® AX and provides key assistance to partners and customers during implementation by providing project management features, easier configuration access, communication support and documentation on how to successfully configure Microsoft Dynamics® AX.

    Key features:

    • Quick and simplified AX Configuration
    • Best Practice documentation for building partner Knowledge Base
    • AX2009 Data Import and Export
    • Project Management via MS Project Integration and Issue Tracking
    • Alerts and Notifications

    Click here to download.

  • mfp's two cents

    Channel 9 Screencast - MorphX Best Practices


    The new installment of AX screencasts is now available on Channel9. It is a 20 minute video on MorphX Best Practices in Dynamics AX 4.0 - one of my favorite topics!

    Here is the link:

    I've attached the PowerPoint slide deck to this post. The deck contains a transcript of the talk in the slide notes.

  • mfp's two cents

    X++ Debugging Tips and Tricks


    X   Debugging - Full 2

    I hate debugging - I would much rather write code or watch tests execute! Debugging is a waste of time, and often follows an unpredictable path.

    Any developer knows the situation: A bug is discovered, and the cause must be found. Most developers have these war stories on how they fought with a bug. The battle swung back and forth. Theories were tested and discarded, instrumentation was built, log files were examined, Murphy visited and, coffee was spilled. When the stories are told, they typically have a happy ending, so eventually the Eureka-moment occurred and the cause was found. And a debugging lesson was learned.

    One of my toughest debugging session spanned almost 2 weeks. My code was complete and ready for check-in. I had worked on it for about 2 months in a private branch. Before reverse integrating into the main branch I ran all unit tests. A successful run takes about 12 hours. In my case the execution crashed after about 10 hours. I tried to run the single test case that crashed in isolation. It passed. I tried again with the full automation. It still crashed. I then tried disabling the crashing test, just to realize 10 hours later, that now a different test crashed. I then tried running with a debug version of AX32, so I could debug the crash. But the debug build is much slower, so it would take ages before the crash happened. So I needed to isolate the crash in a simpler repro. I tried changing the order of tests, and with every change I made the crash moved somewhere else. I then began trawling the system logs for information about the crash. This lead me to think about memory – perhaps something was leaking memory. I instrumented the test automation, so it would dump the current memory footprint every 10 seconds to a log file. And yes – memory consumption climbed until the crash occurred. But hey; I’m just writing X++ code – what can I possibly do that causes memory to leak? Driving home one day I got the idea that perhaps it was not my code, but the X++ runtime. After all; I was using some of the not-yet-released-at-this-point X++ features pretty heavily (attributes and events). So I wrote a small job, that exercised these areas heavily, and I saw memory climbing slowly but steadily until the crash. Now with a simple repro in hand, it was not hard to solve the issue – but I did require help from my friends in the X++ team. Two weeks had passed before I was ready to submit, my code was exactly the same, and now all test passed.

    This X++ debugging series of posts is dedicated to share X++ debugging tips and tricks. Having a toolbox full of different ways to attack bugs is required to be a productive developer. I’m going to share some of my tricks. If you have tricks of your own, please write a comment, and I will include them in the series. The ultimate goal is that we all efficiently find and solve X++ bugs.

    Trick #1 – Breakpoint in Info.add()

    Trick #2 – xSession::xppCallStack()

    Trick #3 – Conditional breakpoints

    Trick #4 – Reading uncommitted data

    Trick #5 – To be announced

  • mfp's two cents

    Microsoft Dynamics AX WMS/TMS Workshop



    Today and tomorrow the SCM team from the Microsoft Dynamics AX R&D division is hosting a workshop on the upcoming Warehouse and Transportation management functionality in Dynamics AX 2012 R3. 140 partners and customers are gather from over 80 different companies at DGI Byen in Copenhagen.

    We have 2 busy days ahead of us with short presentations followed by hands-on-labs. No one will leave Copenhagen without a first hands experience of WMS and TMS including:

    • Reservation Hierarchies,
    • New Inventory dimensions: License Plate and Inventory Status,
    • Product configuration,
    • Warehouse setup of Location, Zones and Profiles,
    • Warehouse flow,
    • Outbound warehouse processes using mobile devices,
    • Warehouse integration with Discrete manufacturing and Kanban,
    • Transportation setup of Carrier, Rating Profiles, Rate Master, Override Charges, and Routes and Routing Constraints.
    • Freight reconciliation
  • mfp's two cents

    Memory consumption of tables variables


    I recently made an interesting discovery, that I want to share with you. I was investigating an recursive algorithm that scaled nicely; until it ran out of memory. For each level of the recursion a new class instance was created – this class contained multiple member variables of type Table (as in CustTable). I decided to measure the memory footprint of this construct, and I was much surprised by my findings.

    Consider these 3 classes – that each stores the same amount of data, but in different ways:

    /// <summary> 
    /// Test class containing a table buffer and setting 20 fields 
    /// </summary> 
    class Class1 
        BOMCalcTrans bomCalcTrans; 
        public void new() 
            bomCalcTrans.ConsumptionVariable = 50; 
            bomCalcTrans.ConsumptionConstant = 50; 
            bomCalcTrans.CostPriceQty = 50; 
            bomCalcTrans.CostPrice = 50; 
            bomCalcTrans.CostPriceQtySecCur_RU = 50; 
            bomCalcTrans.CostMarkup = 50; 
            bomCalcTrans.NumOfSeries = 50; 
            bomCalcTrans.SalesPriceQty = 50; 
            bomCalcTrans.SalesMarkupQty = 50; 
            bomCalcTrans.CostMarkupQty = 50; 
            bomCalcTrans.CostMarkupQtySecCur_RU = 50; 
            bomCalcTrans.CostPriceSecCur_RU = 50; 
            bomCalcTrans.CostMarkupSecCur_RU = 50; 
            bomCalcTrans.NetWeightQty = 50; 
            bomCalcTrans.CostPriceUnit = 50; 
            bomCalcTrans.SalesPrice = 50; 
            bomCalcTrans.SalesMarkup = 50; 
            bomCalcTrans.SalesPriceUnit = 50; 
            bomCalcTrans.SalesPriceFallBackVersion = 'abc'; 
            bomCalcTrans.CostPriceFallBackVersion = 'def'; 
    /// <summary> 
    /// Test class containing a hashTable with 20 key/value pairs 
    /// </summary> 
    class Class2 
        System.Collections.Hashtable hashTable; 
        public void new() 
            hashTable = new System.Collections.Hashtable(); 
            hashTable.Add(fieldNum(bomCalcTrans,ConsumptionVariable), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,ConsumptionConstant), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceQty), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPrice), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceQtySecCur_RU), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkup), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,NumOfSeries), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesPriceQty), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesMarkupQty), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkupQty), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkupQtySecCur_RU), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceSecCur_RU), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkupSecCur_RU), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,NetWeightQty), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceUnit), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesPrice), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesMarkup), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesPriceUnit), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesPriceFallBackVersion), 'abc'); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceFallBackVersion), 'def'); 
    /// <summary> 
    /// Test class containing 20 member variables with values 
    /// </summary> 
    class Class3 
        InventQty bomCalcTransConsumptionVariable; 
        InventQty bomCalcTransConsumptionConstant; 
        CostPrice bomCalcTransCostPriceQty; 
        CostPrice bomCalcTransCostPrice; 
        CostPriceSecCur_RU bomCalcTransCostPriceQtySecCur_RU; 
        CostPrice bomCalcTransCostMarkup; 
        InventQty bomCalcTransNumOfSeries; 
        InventSalesPrice bomCalcTransSalesPriceQty; 
        InventSalesMarkup bomCalcTransSalesMarkupQty; 
        CostMarkup bomCalcTransCostMarkupQty; 
        InventPriceMarkupSecCur_RU bomCalcTransCostMarkupQtySecCur_RU; 
        CostPriceSecCur_RU bomCalcTransCostPriceSecCur_RU; 
        CostPriceSecCur_RU bomCalcTransCostMarkupSecCur_RU; 
        ItemNetWeight bomCalcTransNetWeightQty; 
        PriceUnit bomCalcTransCostPriceUnit; 
        CostingVersionId bomCalcTransCostPriceFallBackVersion; 
        InventSalesPrice bomCalcTransSalesPrice; 
        InventSalesMarkup bomCalcTransSalesMarkup; 
        PriceUnit bomCalcTransSalesPriceUnit; 
        CostingVersionId bomCalcTransSalesPriceFallBackVersion; 
        public void new() 
            bomCalcTransConsumptionVariable = 50; 
            bomCalcTransConsumptionConstant = 50; 
            bomCalcTransCostPriceQty = 50; 
            bomCalcTransCostPrice = 50; 
            bomCalcTransCostPriceQtySecCur_RU = 50; 
            bomCalcTransCostMarkup = 50; 
            bomCalcTransNumOfSeries = 50; 
            bomCalcTransSalesPriceQty = 50; 
            bomCalcTransSalesMarkupQty = 50; 
            bomCalcTransCostMarkupQty = 50; 
            bomCalcTransCostMarkupQtySecCur_RU = 50; 
            bomCalcTransCostPriceSecCur_RU = 50; 
            bomCalcTransCostMarkupSecCur_RU = 50; 
            bomCalcTransNetWeightQty = 50; 
            bomCalcTransCostPriceUnit = 50; 
            bomCalcTransSalesPrice = 50; 
            bomCalcTransSalesMarkup = 50; 
            bomCalcTransSalesPriceUnit = 50; 
            bomCalcTransSalesPriceFallBackVersion = 'abc';  
            bomCalcTransCostPriceFallBackVersion = 'def'; 

    Now; let us create 1,000,000 instances of each and store them all in a List(Types::Class), and measure the memory footprint of each type of the 3 classes – running as IL and running as pcode.


    Notice that using a table as a member variable consumes 6x as much memory as having each field as a member – apparently there is a huge overhead associated with the xRecord+Common functionality.

    My conclusion – apparently you cannot have nice code and low memory consumption at the same time. My advice – be careful when using tables as member variables, especially in batch scenarios, i.e. classes derived from RunBaseBatch or used by same.

  • mfp's two cents

    Debug::Assert in X++


    Update 4th of August 2014: Clarification of the AX runtime behavior when non-developers hit an assert statement.

      defect free software

    “In computer programming, an assertion is a predicate (a true–false statement) placed in a program to indicate that the developer thinks that the predicate is always true at that place.”

    What would you rather have: A piece of source code with or without assertions? I’d definitely prefer source code with assertions – it makes the code easier to read, debug and troubleshoot.

    My recommendation is to use assertions in X++ when all of the following are true:

    1. The condition can never be false if the code is correct, and
    2. The condition is not so trivial it obviously be always true, and
    3. The condition is in some sense internal to the component, and
    4. The condition can be verified without any method calls.


    1. The condition can never be false if the code is correct

    Assertions and error handling are two different things.

    • Assertions should be used to document logically impossible situations in a correctly implemented program. If the impossible occur, then something is wrong – a programming error has been discovered.
    • Error handling – as the name implies – is the implementation of what should happen when an error occurs. Error handling are valid code paths that determines how the software reacts to various erroneous conditions, like incorrect data entered by the user, to system error conditions like out-of-memory.

    In a nutshell, it should be possible to write unit test for error handling – but not for assertions.

    2. The condition is not so trivial it obviously be always true

    Assert statements takes time to write and read – and if the condition they are asserting is obviously always true, then the assertion is pure clutter – and we are better off without it.

    3. The condition is in some sense internal to the component

    A failing assertion is an indication of a problem with the implementation. Something within the component – regardless of input from the outside world – is broken and needs fixing. Typically, I’d use assertions for input validation in private methods, and exceptions in public methods. Conversely, you don’t want consumers of your component to be hit by assertions – regardless of how they use your component.

    4. The condition can be verified without any method calls.

    Assert statements in X++ are a little special, as the X++ compiler always includes assert statements. In other languages (like C#) you can have multiple compiler targets – and typically the release build would not include the assert statements. In AX when a non-developer is hitting an assert statement, then the runtime will suppress eventual errors. I.e. in a production system assert statements have no functional impact.

    Given assert statements in X++ are always evaluated, and thus degrades performance, they should be used with a bit of caution. If the condition can be verified with minimal overhead – for example that a variable has a certain value – then there is no problem. However; if the assertion requires execution of complex logic, RPC or SQL calls then it should be avoided, due to the performance impact. In cases where the performance impact is significant, but you don’t want to compromise on assertions, the assertions can be wrapped inside a call to Debug::debugMode().

    “without any method calls” is just a guiding principles. Sometimes it makes sense to factor the condition into a Boolean method – for reuse or for clarity – here I would not object.


    Here is an example of good use of assertion in X++:

    private void markDetailRecordAsEdited(
    RecId _journalControlDetailId,
    RecId _draftConstraintTreeId) { Debug::assert(_journalControlDetailId != 0); Debug::assert(_draftConstraintTreeId != 0); if (! modifiedDetailRecords.exists(_journalControlDetailId)) { modifiedDetailRecords.insert(
    _draftConstraintTreeId); } }


    Here is another example where Debug::debugMode() is used:

    private void render()
        if (Debug::debugMode())
            Debug::assert(this.hierarchyCount() > 0);
            Debug::assert(segments != null);
            Debug::assert(totalSegmentCount > 0);

    Closing remarks

    I once saw a t-shirt with this print on the front: “If debugging is the process of removing bugs, then programming must be the process of putting them in”.  I wish the back had read: “Programming with assertions is one way to keep bugs out.”

Page 5 of 20 (195 items) «34567»